diff --git a/src/aframe-streetmix-parsers.js b/src/aframe-streetmix-parsers.js
index 2ac9f62a..56d84f17 100644
--- a/src/aframe-streetmix-parsers.js
+++ b/src/aframe-streetmix-parsers.js
@@ -4,6 +4,51 @@
var streetmixParsersTested = require('./tested/aframe-streetmix-parsers-tested');
var { segmentVariants } = require('./segments-variants.js');
+const COLORS = {
+ red: '#ff9393',
+ blue: '#00b6b6',
+ green: '#adff83',
+ yellow: '#f7d117',
+ lightGray: '#dddddd',
+ white: '#ffffff',
+ brown: '#664B00'
+};
+
+const TYPES = {
+ 'drive-lane': {
+ surface: 'asphalt',
+ color: COLORS.white
+ },
+ 'bus-lane': {
+ surface: 'asphalt',
+ color: COLORS.red
+ },
+ 'bike-lane': {
+ surface: 'asphalt',
+ color: COLORS.green
+ },
+ sidewalk: {
+ surface: 'sidewalk',
+ color: COLORS.white
+ },
+ 'parking-lane': {
+ surface: 'concrete',
+ color: COLORS.lightGray
+ },
+ divider: {
+ surface: 'hatched',
+ color: COLORS.white
+ },
+ grass: {
+ surface: 'grass',
+ color: COLORS.white
+ },
+ rail: {
+ surface: 'asphalt',
+ color: COLORS.white
+ }
+};
+
function cloneMixinAsChildren({
objectMixinId = '',
parentEl = null,
@@ -190,7 +235,7 @@ function createRailsElement(length, railsPosX) {
};
placedObjectEl.setAttribute('geometry', railsGeometry);
placedObjectEl.setAttribute('material', railsMaterial);
- placedObjectEl.setAttribute('class', 'rails');
+ placedObjectEl.setAttribute('data-layer-name', 'rails');
placedObjectEl.setAttribute('shadow', 'receive:true; cast: true');
placedObjectEl.setAttribute('position', railsPosX + ' 0.2 0'); // position="1.043 0.100 -3.463"
@@ -199,7 +244,7 @@ function createRailsElement(length, railsPosX) {
function createTracksParentElement(length, objectMixinId) {
const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'track-parent');
+ placedObjectEl.setAttribute('data-layer-name', 'Tracks Parent');
placedObjectEl.setAttribute('position', '0 -0.2 0'); // position="1.043 0.100 -3.463"
// add rails
const railsWidth = {
@@ -214,46 +259,6 @@ function createTracksParentElement(length, objectMixinId) {
return placedObjectEl;
}
-function createBollardsParentElement() {
- const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'bollard-parent');
- return placedObjectEl;
-}
-
-function createParentElement(className) {
- const parentEl = document.createElement('a-entity');
- parentEl.setAttribute('class', className);
- return parentEl;
-}
-
-function createDividerVariant(variantName, clonedObjectRadius, step = 2.25) {
- const dividerParentEl = createParentElement(`dividers-${variantName}-parent`);
- cloneMixinAsChildren({
- objectMixinId: `dividers-${variantName}`,
- parentEl: dividerParentEl,
- step: step,
- radius: clonedObjectRadius
- });
- return dividerParentEl;
-}
-
-function createClonedVariants(
- variantName,
- clonedObjectRadius,
- step = 2.25,
- rotation = '0 0 0'
-) {
- const dividerParentEl = createParentElement(`${variantName}-parent`);
- cloneMixinAsChildren({
- objectMixinId: variantName,
- parentEl: dividerParentEl,
- step: step,
- radius: clonedObjectRadius,
- rotation: rotation
- });
- return dividerParentEl;
-}
-
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
@@ -275,7 +280,6 @@ function getZPositions(start, end, step) {
function createSidewalkClonedVariants(
segmentWidthInMeters,
density,
- elevationPosY = 0,
streetLength,
direction = 'random',
animated = false
@@ -299,8 +303,8 @@ function createSidewalkClonedVariants(
densityFactors[density] * streetLength,
10
);
- const dividerParentEl = createParentElement('pedestrians-parent');
- dividerParentEl.setAttribute('position', { y: elevationPosY });
+ const dividerParentEl = document.createElement('a-entity');
+ dividerParentEl.setAttribute('data-layer-name', 'Pedestrians Parent');
// Randomly generate avatars
for (let i = 0; i < totalPedestrianNumber; i++) {
const variantName =
@@ -327,7 +331,6 @@ function createSidewalkClonedVariants(
1.4,
streetLength,
xVal,
- yVal,
zVal,
animationDirection
);
@@ -338,30 +341,17 @@ function createSidewalkClonedVariants(
return dividerParentEl;
}
-function getBikeLaneMixin(variant) {
- if (variant === 'red') {
- return 'surface-red bike-lane';
+function getSegmentColor(variant) {
+ if ((variant === 'red') | (variant === 'colored')) {
+ return COLORS.red;
}
if (variant === 'blue') {
- return 'surface-blue bike-lane';
+ return COLORS.blue;
}
- if (variant === 'green') {
- return 'surface-green bike-lane';
+ if ((variant === 'green') | (variant === 'grass')) {
+ return COLORS.green;
}
- return 'bike-lane';
-}
-
-function getBusLaneMixin(variant) {
- if ((variant === 'colored') | (variant === 'red')) {
- return 'surface-red bus-lane';
- }
- if (variant === 'blue') {
- return 'surface-blue bus-lane';
- }
- if (variant === 'grass') {
- return 'surface-green bus-lane';
- }
- return 'bus-lane';
+ return COLORS.white;
}
function getDimensions(object3d) {
@@ -394,49 +384,11 @@ function randomPosition(entity, axis, length, objSizeAttr = undefined) {
return newPosition;
}
-function createChooChooElement(
- variantList,
- objectMixinId,
- length,
- showVehicles
-) {
- if (!showVehicles) {
- return;
- }
- const rotationY = variantList[0] === 'inbound' ? 0 : 180;
- const placedObjectEl = document.createElement('a-entity');
- const tramLength = 23;
- placedObjectEl.setAttribute('rotation', '0 ' + rotationY + ' 0');
- placedObjectEl.setAttribute('mixin', objectMixinId);
- placedObjectEl.setAttribute('class', objectMixinId);
- const positionZ = randomPosition(placedObjectEl, 'z', length, tramLength);
- placedObjectEl.setAttribute('position', '0 0 ' + positionZ);
- return placedObjectEl;
-}
-
-function createBusElement(variantList, length, showVehicles) {
- if (!showVehicles) {
- return;
- }
- const rotationY = variantList[0] === 'inbound' ? 0 : 180;
- const busParentEl = document.createElement('a-entity');
- const busLength = 12;
- const busObjectEl = document.createElement('a-entity');
- busObjectEl.setAttribute('rotation', '0 ' + rotationY + ' 0');
- busObjectEl.setAttribute('mixin', 'bus');
- const positionZ = randomPosition(busObjectEl, 'z', length, busLength);
- busObjectEl.setAttribute('position', '0 0 ' + positionZ);
- busParentEl.append(busObjectEl);
-
- return busParentEl;
-}
-
function addLinearStreetAnimation(
reusableObjectEl,
speed,
streetLength,
xPos,
- yVal = 0,
zPos,
direction
) {
@@ -450,7 +402,7 @@ function addLinearStreetAnimation(
property: 'position',
easing: 'linear',
loop: 'false',
- from: { x: xPos, y: yVal, z: zPos },
+ from: { x: xPos, y: 0, z: zPos },
to: { z: halfStreet },
dur: startingDuration
};
@@ -458,8 +410,8 @@ function addLinearStreetAnimation(
property: 'position',
easing: 'linear',
loop: 'true',
- from: { x: xPos, y: yVal, z: -halfStreet },
- to: { x: xPos, y: yVal, z: halfStreet },
+ from: { x: xPos, y: 0, z: -halfStreet },
+ to: { x: xPos, y: 0, z: halfStreet },
delay: startingDuration,
dur: totalStreetDuration
};
@@ -511,7 +463,6 @@ function createDriveLaneElement(
return createSidewalkClonedVariants(
segmentWidthInMeters,
'normal',
- 0,
streetLength,
direction,
animated
@@ -585,7 +536,6 @@ function createDriveLaneElement(
speed,
streetLength,
0,
- 0,
positionZ,
direction
);
@@ -629,158 +579,6 @@ function createDriveLaneElement(
return driveLaneParentEl;
}
-function createFoodTruckElement(variantList, length) {
- const foodTruckParentEl = document.createElement('a-entity');
-
- const reusableObjectEl = document.createElement('a-entity');
- const foodTruckLength = 7;
- const rotationY = variantList[0] === 'left' ? 0 : 180;
- reusableObjectEl.setAttribute('rotation', '0 ' + rotationY + ' 0');
- reusableObjectEl.setAttribute('mixin', 'food-trailer-rig');
-
- const positionZ = randomPosition(
- reusableObjectEl,
- 'z',
- length,
- foodTruckLength
- );
- reusableObjectEl.setAttribute('positon', '0 0 ' + positionZ);
- foodTruckParentEl.append(reusableObjectEl);
-
- return foodTruckParentEl;
-}
-
-function createMagicCarpetElement(showVehicles) {
- if (!showVehicles) {
- return;
- }
- const magicCarpetParentEl = document.createElement('a-entity');
-
- const reusableObjectEl1 = document.createElement('a-entity');
- reusableObjectEl1.setAttribute('position', '0 1.75 0');
- reusableObjectEl1.setAttribute('rotation', '0 0 0');
- reusableObjectEl1.setAttribute('mixin', 'magic-carpet');
- magicCarpetParentEl.append(reusableObjectEl1);
- const reusableObjectEl2 = document.createElement('a-entity');
- reusableObjectEl2.setAttribute('position', '0 1.75 0');
- reusableObjectEl2.setAttribute('rotation', '0 0 0');
- reusableObjectEl2.setAttribute('mixin', 'Character_1_M');
- magicCarpetParentEl.append(reusableObjectEl2);
-
- return magicCarpetParentEl;
-}
-
-function randPlacedElements(streetLength, objLength, count) {
- const placeLength = objLength / 2 + objLength;
- const allPlaces = getZPositions(
- -streetLength / 2 + placeLength / 2,
- streetLength / 2 - placeLength / 2,
- placeLength
- );
- return allPlaces.slice(0, count);
-}
-
-function createOutdoorDining(length, posY) {
- const outdoorDiningParentEl = document.createElement('a-entity');
- const outdorDiningLength = 2.27;
-
- const randPlaces = randPlacedElements(length, outdorDiningLength, 5);
- randPlaces.forEach((randPosZ) => {
- const reusableObjectEl = document.createElement('a-entity');
- reusableObjectEl.setAttribute('mixin', 'outdoor_dining');
-
- // const positionZ = randomPosition(reusableObjectEl, 'z', length, outdorDiningLength);
- reusableObjectEl.setAttribute('position', { y: posY, z: randPosZ });
- outdoorDiningParentEl.append(reusableObjectEl);
- });
-
- return outdoorDiningParentEl;
-}
-
-function createMicroMobilityElement(
- variantList,
- segmentType,
- posY = 0,
- length,
- showVehicles,
- animated = false
-) {
- if (!showVehicles) {
- return;
- }
- const microMobilityParentEl = document.createElement('a-entity');
-
- const bikeLength = 2.03;
- const bikeCount = getRandomIntInclusive(2, 5);
-
- const cyclistMixins = [
- 'cyclist-cargo',
- 'cyclist1',
- 'cyclist2',
- 'cyclist3',
- 'cyclist-dutch',
- 'cyclist-kid'
- ];
-
- const countCyclist = cyclistMixins.length;
- let mixinId = 'Bicycle_1';
- const randPlaces = randPlacedElements(length, bikeLength, bikeCount);
- randPlaces.forEach((randPosZ) => {
- const reusableObjectEl = document.createElement('a-entity');
- const rotationY = variantList[0] === 'inbound' ? 0 : 180;
- reusableObjectEl.setAttribute('rotation', '0 ' + rotationY + ' 0');
- reusableObjectEl.setAttribute('position', { y: posY, z: randPosZ });
-
- if (animated) {
- reusableObjectEl.setAttribute('animation-mixer', '');
- const speed = 5;
- addLinearStreetAnimation(
- reusableObjectEl,
- speed,
- length,
- 0,
- posY,
- randPosZ,
- variantList[0]
- );
- }
- if (segmentType === 'bike-lane') {
- mixinId = cyclistMixins[getRandomIntInclusive(0, countCyclist)];
- } else {
- mixinId = 'ElectricScooter_1';
- }
-
- reusableObjectEl.setAttribute('mixin', mixinId);
- microMobilityParentEl.append(reusableObjectEl);
- });
-
- return microMobilityParentEl;
-}
-
-function createFlexZoneElement(variantList, length, showVehicles = true) {
- if (!showVehicles) {
- return;
- }
- const flexZoneParentEl = document.createElement('a-entity');
- const carLength = 5;
- const carCount = getRandomIntInclusive(2, 4);
- const randPlaces = randPlacedElements(length, carLength, carCount);
- randPlaces.forEach((randPosZ) => {
- const reusableObjectEl = document.createElement('a-entity');
- const rotationY = variantList[1] === 'inbound' ? 0 : 180;
- reusableObjectEl.setAttribute('rotation', '0 ' + rotationY + ' 0');
- if (variantList[0] === 'taxi') {
- reusableObjectEl.setAttribute('mixin', 'sedan-taxi-rig');
- } else if (variantList[0] === 'rideshare') {
- reusableObjectEl.setAttribute('mixin', 'sedan-rig');
- }
- reusableObjectEl.setAttribute('position', { z: randPosZ });
- flexZoneParentEl.append(reusableObjectEl);
- });
-
- return flexZoneParentEl;
-}
-
function createWayfindingElements() {
const wayfindingParentEl = document.createElement('a-entity');
let reusableObjectEl;
@@ -812,80 +610,6 @@ function createWayfindingElements() {
return wayfindingParentEl;
}
-function createBenchesParentElement() {
- const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'bench-parent');
- // y = 0.2 for sidewalk elevation
- placedObjectEl.setAttribute('position', '0 0.2 3.5');
- return placedObjectEl;
-}
-
-function createBikeRacksParentElement(posY) {
- const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'bikerack-parent');
- placedObjectEl.setAttribute('position', { y: posY, z: -3.5 });
- return placedObjectEl;
-}
-
-function createBikeShareStationElement(variantList, posY) {
- const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'bikeshare');
- placedObjectEl.setAttribute('mixin', 'bikeshare');
- const rotationCloneY = variantList[0] === 'left' ? 90 : 270;
- placedObjectEl.setAttribute('rotation', '0 ' + rotationCloneY + ' 0');
- placedObjectEl.setAttribute('position', { y: posY });
- return placedObjectEl;
-}
-
-function createParkletElement(length, variantList) {
- const parkletParent = document.createElement('a-entity');
- const parkletLength = 4.03;
- const parkletCount = 3;
- const randPlaces = randPlacedElements(length, parkletLength, parkletCount);
- randPlaces.forEach((randPosZ) => {
- const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'parklet');
- placedObjectEl.setAttribute('position', { x: 0, y: 0.02, z: randPosZ });
- placedObjectEl.setAttribute('mixin', 'parklet');
- const rotationY = variantList[0] === 'left' ? 90 : 270;
- placedObjectEl.setAttribute('rotation', { y: rotationY });
- parkletParent.append(placedObjectEl);
- });
- return parkletParent;
-}
-
-function createTreesParentElement() {
- const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'tree-parent');
- // y = 0.2 for sidewalk elevation
- placedObjectEl.setAttribute('position', '0 0.2 7');
- return placedObjectEl;
-}
-
-function createLampsParentElement() {
- const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'lamp-parent');
- // y = 0.2 for sidewalk elevation
- placedObjectEl.setAttribute('position', '0 0.2 0'); // position="1.043 0.100 -3.463"
- return placedObjectEl;
-}
-
-function createBusStopElement(rotationBusStopY, posY) {
- const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'bus-stop');
- placedObjectEl.setAttribute('rotation', '0 ' + rotationBusStopY + ' 0');
- placedObjectEl.setAttribute('mixin', 'bus-stop');
- placedObjectEl.setAttribute('position', { y: posY });
- return placedObjectEl;
-}
-
-function createBrtStationElement() {
- const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'brt-station');
- placedObjectEl.setAttribute('mixin', 'brt-station');
- return placedObjectEl;
-}
-
// offset to center the street around global x position of 0
function createCenteredStreetElement(segments) {
const streetEl = document.createElement('a-entity');
@@ -898,42 +622,12 @@ function createCenteredStreetElement(segments) {
return streetEl;
}
-function createSegmentElement(
- segmentWidthInMeters,
- positionY,
- mixinId,
- length,
- repeatCount,
- elevation = 0
-) {
- var segmentEl = document.createElement('a-entity');
- const heightLevels = [0.2, 0.4, 0.6];
- const height = heightLevels[elevation];
- if (elevation === 0) {
- positionY = -0.1;
- } else if (elevation === 2) {
- positionY = 0.1;
- }
-
- segmentEl.setAttribute(
- 'geometry',
- `primitive: box;
- height: ${height};
- depth: ${length};
- width: ${segmentWidthInMeters};`
- );
-
- segmentEl.setAttribute('position', { y: positionY });
- segmentEl.setAttribute('mixin', mixinId);
-
- if (repeatCount.length !== 0) {
- segmentEl.setAttribute(
- 'material',
- `repeat: ${repeatCount[0]} ${repeatCount[1]}`
- );
+function calculateHeight(elevation) {
+ const stepLevel = 0.15;
+ if (elevation <= 0) {
+ return stepLevel;
}
-
- return segmentEl;
+ return stepLevel * (elevation + 1);
}
function createSeparatorElement(
@@ -951,7 +645,9 @@ function createSeparatorElement(
segmentEl.setAttribute('rotation', '270 ' + rotationY + ' 0');
segmentEl.setAttribute('scale', scalePlane);
- segmentEl.setAttribute('position', '0 ' + positionY + ' 0');
+ let posY = calculateHeight(elevation) + positionY;
+ // take into account elevation property and add to positionY
+ segmentEl.setAttribute('position', '0 ' + posY + ' 0');
segmentEl.setAttribute('mixin', mixinId);
if (repeatCount.length !== 0) {
@@ -1013,6 +709,7 @@ function processSegments(
var cumulativeWidthInMeters = 0;
for (var i = 0; i < segments.length; i++) {
+ var segmentColor = null;
var segmentParentEl = document.createElement('a-entity');
segmentParentEl.classList.add('segment-parent-' + i);
@@ -1034,12 +731,6 @@ function processSegments(
// elevation property from streetmix segment
const elevation = segments[i].elevation;
- const elevationLevels = [0, 0.2, 0.4];
- const elevationPosY = elevationLevels[elevation];
-
- // add y elevation position as a data attribute to segment entity
- segmentParentEl.setAttribute('data-elevation-posY', elevationPosY);
-
// Note: segment 3d models are outbound by default
// If segment variant inbound, rotate segment model by 180 degrees
var rotationY =
@@ -1048,37 +739,28 @@ function processSegments(
variantList[0] === 'outbound' || variantList[1] === 'outbound' ? 1 : -1;
// the A-Frame mixin ID is often identical to the corresponding streetmix segment "type" by design, let's start with that
- var groundMixinId = segments[i].type;
+ var segmentPreset = segments[i].type;
// repeat value for material property - repeatCount[0] is x texture repeat and repeatCount[1] is y texture repeat
const repeatCount = [];
// look at segment type and variant(s) to determine specific cases
if (segments[i].type === 'drive-lane' && variantList[1] === 'sharrow') {
- // make a parent entity for the stencils
- const stencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015
- });
- // clone a bunch of stencil entities (note: this is not draw call efficient)
- cloneMixinAsChildren({
- objectMixinId: 'stencils sharrow',
- parentEl: stencilsParentEl,
- rotation: '-90 ' + rotationY + ' 0',
- step: 10,
- radius: clonedObjectRadius
- });
- // add this stencil stuff to the segment parent
- segmentParentEl.append(stencilsParentEl);
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: stencils sharrow; length: ${length}; rotationX: -90; positionY: 0.15; cycleOffset: 0.2; spacing: 15;`
+ );
} else if (
segments[i].type === 'bike-lane' ||
segments[i].type === 'scooter'
) {
+ segmentPreset = 'bike-lane'; // use bike lane road material
// make a parent entity for the stencils
const stencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015
+ y: 0.015
});
// get the mixin id for a bike lane
- groundMixinId = getBikeLaneMixin(variantList[1]);
+ segmentColor = getSegmentColor(variantList[1]);
// clone a bunch of stencil entities (note: this is not draw call efficient)
cloneMixinAsChildren({
objectMixinId: 'stencils bike-arrow',
@@ -1089,36 +771,49 @@ function processSegments(
});
// add this stencil stuff to the segment parent
segmentParentEl.append(stencilsParentEl);
- segmentParentEl.append(
- createMicroMobilityElement(
- variantList,
- segments[i].type,
- elevationPosY,
- length,
- showVehicles,
- globalAnimated
- )
+ const rotationCloneY = variantList[0] === 'inbound' ? 0 : 180;
+ segmentParentEl.setAttribute(
+ 'street-generated-random',
+ `modelsArray: cyclist-cargo, cyclist1, cyclist2, cyclist3, cyclist-dutch, cyclist-kid${segments[i].type === 'scooter' ? 'ElectricScooter_1' : ''};
+ length: ${length};
+ placeLength: 2.03;
+ facing: ${rotationCloneY};
+ count: ${getRandomIntInclusive(2, 5)};`
);
} else if (
segments[i].type === 'light-rail' ||
segments[i].type === 'streetcar'
) {
- // get the mixin id for a bus lane
- groundMixinId = getBusLaneMixin(variantList[1]);
+ segmentPreset = 'rail';
+ // get the color for a bus lane
+ segmentColor = getSegmentColor(variantList[1]);
// get the mixin id for the vehicle (is it a trolley or a tram?)
- var objectMixinId = segments[i].type === 'streetcar' ? 'trolley' : 'tram';
- // create and append a train element
- segmentParentEl.append(
- createChooChooElement(variantList, objectMixinId, length, showVehicles)
- );
+ const objectMixinId =
+ segments[i].type === 'streetcar' ? 'trolley' : 'tram';
+ if (showVehicles) {
+ segmentParentEl.setAttribute(
+ 'street-generated-random',
+ `model: ${objectMixinId}; length: ${length}; placeLength: 23; facing: ${rotationY}; count: 1;`
+ );
+ }
// make the parent for all the objects to be cloned
const tracksParentEl = createTracksParentElement(length, objectMixinId);
// add these trains to the segment parent
segmentParentEl.append(tracksParentEl);
} else if (segments[i].type === 'turn-lane') {
- groundMixinId = 'drive-lane'; // use normal drive lane road material
+ segmentPreset = 'drive-lane'; // use normal drive lane road material
+ if (showVehicles) {
+ const rotationCloneY = variantList[0] === 'inbound' ? 0 : 180;
+ segmentParentEl.setAttribute(
+ 'street-generated-random',
+ `modelsArray: sedan-rig, box-truck-rig, self-driving-waymo-car, suv-rig, motorbike;
+ length: ${length};
+ placeLength: 7.3;
+ facing: ${rotationCloneY};
+ count: ${getRandomIntInclusive(2, 4)};`
+ );
+ }
var markerMixinId = variantList[1]; // set the mixin of the road markings to match the current variant name
-
// Fix streetmix inbound turn lane orientation (change left to right) per: https://github.com/streetmix/streetmix/issues/683
if (variantList[0] === 'inbound') {
markerMixinId = markerMixinId.replace(/left|right/g, function (m) {
@@ -1135,7 +830,7 @@ function processSegments(
// make the parent for all the objects to be cloned
const stencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015
+ y: 0.015
});
cloneMixinAsChildren({
objectMixinId: mixinString,
@@ -1149,7 +844,7 @@ function processSegments(
if (variantList[1] === 'shared') {
// add an additional marking to represent the opposite turn marking stencil (rotated 180º)
const stencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015,
+ y: 0.015,
z: -3 * isOutbound
});
cloneMixinAsChildren({
@@ -1163,137 +858,123 @@ function processSegments(
segmentParentEl.append(stencilsParentEl);
}
} else if (segments[i].type === 'divider' && variantList[0] === 'bollard') {
- groundMixinId = 'divider';
+ segmentPreset = 'divider';
// make some bollards
- const bollardsParentEl = createBollardsParentElement();
- cloneMixinAsChildren({
- objectMixinId: 'bollard',
- parentEl: bollardsParentEl,
- step: 4,
- radius: clonedObjectRadius
- });
- // add the bollards to the segment parent
- segmentParentEl.append(bollardsParentEl);
- repeatCount[0] = 1;
- repeatCount[1] = parseInt(length) / 4;
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: bollard; spacing: 4; length: ${length}`
+ );
} else if (segments[i].type === 'divider' && variantList[0] === 'flowers') {
- groundMixinId = 'grass';
- segmentParentEl.append(
- createDividerVariant('flowers', clonedObjectRadius, 2.25)
+ segmentPreset = 'grass';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: dividers-flowers; spacing: 2.25; length: ${length}`
);
} else if (
segments[i].type === 'divider' &&
variantList[0] === 'planting-strip'
) {
- groundMixinId = 'grass';
- segmentParentEl.append(
- createDividerVariant('planting-strip', clonedObjectRadius, 2.25)
+ segmentPreset = 'grass';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: dividers-planting-strip; spacing: 2.25; length: ${length}`
);
} else if (
segments[i].type === 'divider' &&
variantList[0] === 'planter-box'
) {
- groundMixinId = 'grass';
- segmentParentEl.append(
- createDividerVariant('planter-box', clonedObjectRadius, 2.45)
+ segmentPreset = 'grass';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: dividers-planter-box; spacing: 2.45; length: ${length}`
);
} else if (
segments[i].type === 'divider' &&
variantList[0] === 'palm-tree'
) {
- groundMixinId = 'grass';
- const treesParentEl = createTreesParentElement();
- cloneMixinAsChildren({
- objectMixinId: 'palm-tree',
- parentEl: treesParentEl,
- randomY: true,
- radius: clonedObjectRadius
- });
- segmentParentEl.append(treesParentEl);
+ segmentPreset = 'grass';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: palm-tree; length: ${length}`
+ );
} else if (
segments[i].type === 'divider' &&
variantList[0] === 'big-tree'
) {
- groundMixinId = 'grass';
- const treesParentEl = createTreesParentElement();
- cloneMixinAsChildren({
- objectMixinId: 'tree3',
- parentEl: treesParentEl,
- randomY: true,
- radius: clonedObjectRadius
- });
- segmentParentEl.append(treesParentEl);
+ segmentPreset = 'grass';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: tree3; length: ${length}`
+ );
} else if (segments[i].type === 'divider' && variantList[0] === 'bush') {
- groundMixinId = 'grass';
- segmentParentEl.append(
- createDividerVariant('bush', clonedObjectRadius, 2.25)
+ segmentPreset = 'grass';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: dividers-bush; spacing: 2.25; length: ${length}`
);
} else if (segments[i].type === 'divider' && variantList[0] === 'dome') {
- groundMixinId = 'divider';
- segmentParentEl.append(
- createDividerVariant('dome', clonedObjectRadius, 2.25)
+ segmentPreset = 'divider';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: dividers-dome; spacing: 2.25; length: ${length}`
);
- repeatCount[0] = 1;
- repeatCount[1] = parseInt(length) / 4;
} else if (segments[i].type === 'divider') {
- groundMixinId = 'divider';
- repeatCount[0] = 1;
- repeatCount[1] = parseInt(length) / 4;
+ segmentPreset = 'divider';
} else if (
segments[i].type === 'temporary' &&
variantList[0] === 'barricade'
) {
- groundMixinId = 'drive-lane';
- segmentParentEl.append(
- createClonedVariants('temporary-barricade', clonedObjectRadius, 2.25)
+ segmentPreset = 'drive-lane';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: temporary-barricade; spacing: 2.25; length: ${length}`
);
} else if (
segments[i].type === 'temporary' &&
variantList[0] === 'traffic-cone'
) {
- groundMixinId = 'drive-lane';
- segmentParentEl.append(
- createClonedVariants('temporary-traffic-cone', clonedObjectRadius, 2.25)
+ segmentPreset = 'drive-lane';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: temporary-traffic-cone; spacing: 2.25; length: ${length}`
);
} else if (
segments[i].type === 'temporary' &&
variantList[0] === 'jersey-barrier-plastic'
) {
- groundMixinId = 'drive-lane';
- segmentParentEl.append(
- createClonedVariants(
- 'temporary-jersey-barrier-plastic',
- clonedObjectRadius,
- 2.25
- )
+ segmentPreset = 'drive-lane';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: jersey-barrier-plastic; spacing: 2.25; length: ${length}`
);
} else if (
segments[i].type === 'temporary' &&
variantList[0] === 'jersey-barrier-concrete'
) {
- groundMixinId = 'drive-lane';
- segmentParentEl.append(
- createClonedVariants(
- 'temporary-jersey-barrier-concrete',
- clonedObjectRadius,
- 2.93
- )
+ segmentPreset = 'drive-lane';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: temporary-jersey-barrier-concrete; spacing: 2.93; length: ${length}`
);
} else if (
segments[i].type === 'bus-lane' ||
segments[i].type === 'brt-lane'
) {
- groundMixinId = getBusLaneMixin(variantList[1]);
-
- segmentParentEl.append(
- createBusElement(variantList, length, showVehicles)
- );
-
+ // get the color for a bus lane
+ segmentColor = getSegmentColor(variantList[1]);
+
+ if (showVehicles) {
+ const rotationY = variantList[0] === 'inbound' ? 0 : 180;
+ segmentParentEl.setAttribute(
+ 'street-generated-random',
+ `model: bus; length: ${length}; placeLength: 15; facing: ${rotationY}; count: 1;`
+ );
+ }
// create parent for the bus lane stencils to rotate the phrase instead of the word
let reusableObjectStencilsParentEl;
reusableObjectStencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015
+ y: 0.015
});
cloneMixinAsChildren({
objectMixinId: 'stencils word-bus',
@@ -1306,7 +987,7 @@ function processSegments(
segmentParentEl.append(reusableObjectStencilsParentEl);
reusableObjectStencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015,
+ y: 0.015,
z: 10
});
cloneMixinAsChildren({
@@ -1320,7 +1001,7 @@ function processSegments(
segmentParentEl.append(reusableObjectStencilsParentEl);
reusableObjectStencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015,
+ y: 0.015,
z: 20
});
cloneMixinAsChildren({
@@ -1333,33 +1014,39 @@ function processSegments(
// add this stencil stuff to the segment parent
segmentParentEl.append(reusableObjectStencilsParentEl);
} else if (segments[i].type === 'drive-lane') {
- const isAnimated = variantList[2] === 'animated' || globalAnimated;
- const count = getRandomIntInclusive(2, 3);
- const carStep = 7.3;
- segmentParentEl.append(
- createDriveLaneElement(
- variantList,
- segmentWidthInMeters,
- length,
- isAnimated,
- showVehicles,
- count,
- carStep
- )
- );
+ if (showVehicles) {
+ // const isAnimated = variantList[2] === 'animated' || globalAnimated;
+ const rotationCloneY = variantList[0] === 'inbound' ? 0 : 180;
+ segmentParentEl.setAttribute(
+ 'street-generated-random',
+ `modelsArray: sedan-rig, box-truck-rig, self-driving-waymo-car, suv-rig, motorbike;
+ length: ${length};
+ placeLength: 7.3;
+ facing: ${rotationCloneY};
+ count: ${getRandomIntInclusive(2, 4)};`
+ );
+ }
} else if (segments[i].type === 'food-truck') {
- groundMixinId = 'drive-lane';
- segmentParentEl.append(createFoodTruckElement(variantList, length));
- } else if (segments[i].type === 'flex-zone') {
- groundMixinId = 'bright-lane';
- segmentParentEl.append(
- createFlexZoneElement(variantList, length, showVehicles)
+ segmentPreset = 'drive-lane';
+ const rotationCloneY = variantList[0] === 'left' ? 0 : 180;
+ segmentParentEl.setAttribute(
+ 'street-generated-random',
+ `model: food-trailer-rig; length: ${length}; placeLength: 7; facing: ${rotationCloneY}; count: 2;`
);
-
+ } else if (segments[i].type === 'flex-zone') {
+ segmentPreset = 'parking-lane';
+ if (showVehicles) {
+ const objectMixinId =
+ variantList[0] === 'taxi' ? 'sedan-taxi-rig' : 'sedan-rig';
+ const rotationCloneY = variantList[1] === 'inbound' ? 0 : 180;
+ segmentParentEl.setAttribute(
+ 'street-generated-random',
+ `model: ${objectMixinId}; length: ${length}; placeLength: 5; facing: ${rotationCloneY}; count: 4;`
+ );
+ }
let reusableObjectStencilsParentEl;
-
reusableObjectStencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015,
+ y: 0.015,
z: 5
});
cloneMixinAsChildren({
@@ -1373,7 +1060,7 @@ function processSegments(
segmentParentEl.append(reusableObjectStencilsParentEl);
reusableObjectStencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015,
+ y: 0.015,
z: -5
});
cloneMixinAsChildren({
@@ -1392,7 +1079,6 @@ function processSegments(
createSidewalkClonedVariants(
segmentWidthInMeters,
variantList[0],
- elevationPosY,
length,
'random',
isAnimated
@@ -1401,174 +1087,150 @@ function processSegments(
} else if (segments[i].type === 'sidewalk-wayfinding') {
segmentParentEl.append(createWayfindingElements());
} else if (segments[i].type === 'sidewalk-bench') {
- // make the parent for all the benches
- const benchesParentEl = createBenchesParentElement();
-
const rotationCloneY = variantList[0] === 'right' ? -90 : 90;
if (variantList[0] === 'center') {
- cloneMixinAsChildren({
- objectMixinId: 'bench_orientation_center',
- parentEl: benchesParentEl,
- rotation: '0 ' + rotationCloneY + ' 0',
- radius: clonedObjectRadius
- });
- // add benches to the segment parent
- segmentParentEl.append(benchesParentEl);
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: bench_orientation_center; length: ${length}; facing: ${rotationCloneY}; cycleOffset: 0.1`
+ );
} else {
// `right` or `left` bench
- cloneMixinAsChildren({
- objectMixinId: 'bench',
- parentEl: benchesParentEl,
- rotation: '0 ' + rotationCloneY + ' 0',
- radius: clonedObjectRadius
- });
- // add benches to the segment parent
- segmentParentEl.append(benchesParentEl);
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: bench; length: ${length}; facing: ${rotationCloneY}; cycleOffset: 0.1`
+ );
}
} else if (segments[i].type === 'sidewalk-bike-rack') {
- // make the parent for all the bike racks
- const bikeRacksParentEl = createBikeRacksParentElement(elevationPosY);
-
const rotationCloneY = variantList[1] === 'sidewalk-parallel' ? 90 : 0;
- cloneMixinAsChildren({
- objectMixinId: 'bikerack',
- parentEl: bikeRacksParentEl,
- rotation: '0 ' + rotationCloneY + ' 0',
- radius: clonedObjectRadius
- });
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: bikerack; length: ${length}; facing: ${rotationCloneY}; cycleOffset: 0.2`
+ );
// add bike racks to the segment parent
- segmentParentEl.append(bikeRacksParentEl);
} else if (segments[i].type === 'magic-carpet') {
- groundMixinId = 'drive-lane';
- segmentParentEl.append(createMagicCarpetElement(showVehicles));
+ segmentPreset = 'drive-lane';
+ segmentParentEl.setAttribute(
+ 'street-generated-single',
+ `model: magic-carpet;
+ length: ${length};
+ positionY: 1.2;`
+ );
+ segmentParentEl.setAttribute(
+ 'street-generated-single__2',
+ `model: Character_1_M;
+ length: ${length};
+ positionY: 1.2;`
+ );
} else if (segments[i].type === 'outdoor-dining') {
- groundMixinId = variantList[1] === 'road' ? 'drive-lane' : 'sidewalk';
- segmentParentEl.append(createOutdoorDining(length, elevationPosY));
+ segmentPreset = variantList[1] === 'road' ? 'drive-lane' : 'sidewalk';
+ segmentParentEl.setAttribute(
+ 'street-generated-random',
+ `model: outdoor_dining; length: ${length}; placeLength: 2.27; count: 5;`
+ );
} else if (segments[i].type === 'parklet') {
- groundMixinId = 'drive-lane';
- segmentParentEl.append(createParkletElement(length, variantList));
+ segmentPreset = 'drive-lane';
+ const rotationCloneY = variantList[0] === 'left' ? 90 : 270;
+ segmentParentEl.setAttribute(
+ 'street-generated-random',
+ `model: parklet; length: ${length}; placeLength: 4; count: 3; facing: ${rotationCloneY};`
+ );
} else if (segments[i].type === 'bikeshare') {
- // make the parent for all the stations
- segmentParentEl.append(
- createBikeShareStationElement(variantList, elevationPosY)
+ const rotationCloneY = variantList[0] === 'left' ? 90 : 270;
+ segmentParentEl.setAttribute(
+ 'street-generated-single',
+ `model: bikeshare; length: ${length}; facing: ${rotationCloneY}; justify: middle;`
);
} else if (segments[i].type === 'utilities') {
- var rotation = variantList[0] === 'right' ? '0 180 0' : '0 0 0';
- const utilityPoleElems = createClonedVariants(
- 'utility_pole',
- clonedObjectRadius,
- 15,
- rotation
+ const rotationCloneY = variantList[0] === 'right' ? 180 : 0;
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: utility_pole; length: ${length}; cycleOffset: 0.25; facing: ${rotationCloneY}`
);
- segmentParentEl.append(utilityPoleElems);
} else if (segments[i].type === 'sidewalk-tree') {
- // make the parent for all the trees
- const treesParentEl = createTreesParentElement();
- if (variantList[0] === 'palm-tree') {
- objectMixinId = 'palm-tree';
- } else {
- objectMixinId = 'tree3';
- }
- // clone a bunch of trees under the parent
- cloneMixinAsChildren({
- objectMixinId: objectMixinId,
- parentEl: treesParentEl,
- randomY: true,
- radius: clonedObjectRadius
- });
- segmentParentEl.append(treesParentEl);
+ const objectMixinId =
+ variantList[0] === 'palm-tree' ? 'palm-tree' : 'tree3';
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: ${objectMixinId}; length: ${length}; randomFacing: true;`
+ );
} else if (
segments[i].type === 'sidewalk-lamp' &&
(variantList[1] === 'modern' || variantList[1] === 'pride')
) {
- // Make the parent object for all the lamps
- const lampsParentEl = createLampsParentElement();
if (variantList[0] === 'both') {
- cloneMixinAsChildren({
- objectMixinId: 'lamp-modern-double',
- parentEl: lampsParentEl,
- rotation: '0 0 0',
- radius: clonedObjectRadius
- });
- segmentParentEl.append(lampsParentEl);
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: lamp-modern-double; length: ${length}; cycleOffset: 0.4;`
+ );
} else {
var rotationCloneY = variantList[0] === 'right' ? 0 : 180;
- cloneMixinAsChildren({
- objectMixinId: 'lamp-modern',
- parentEl: lampsParentEl,
- rotation: '0 ' + rotationCloneY + ' 0',
- radius: clonedObjectRadius
- });
- segmentParentEl.append(lampsParentEl);
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: lamp-modern; length: ${length}; facing: ${rotationCloneY}; cycleOffset: 0.4;`
+ );
}
// Add the pride flags to the lamp posts
if (
variantList[1] === 'pride' &&
(variantList[0] === 'right' || variantList[0] === 'both')
) {
- cloneMixinAsChildren({
- objectMixinId: 'pride-flag',
- parentEl: lampsParentEl,
- positionXYString: '0.409 5',
- radius: clonedObjectRadius
- });
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed__2',
+ `model: pride-flag; length: ${length}; cycleOffset: 0.4; positionX: 0.409; positionY: 5;`
+ );
}
if (
variantList[1] === 'pride' &&
(variantList[0] === 'left' || variantList[0] === 'both')
) {
- cloneMixinAsChildren({
- objectMixinId: 'pride-flag',
- parentEl: lampsParentEl,
- rotation: '0 -180 0',
- positionXYString: '-0.409 5',
- radius: clonedObjectRadius
- });
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed__2',
+ `model: pride-flag; length: ${length}; facing: 180; cycleOffset: 0.4; positionX: -0.409; positionY: 5;`
+ );
}
} else if (
segments[i].type === 'sidewalk-lamp' &&
variantList[1] === 'traditional'
) {
- // make the parent for all the lamps
- const lampsParentEl = createLampsParentElement();
- // clone a bunch of lamps under the parent
- cloneMixinAsChildren({
- objectMixinId: 'lamp-traditional',
- parentEl: lampsParentEl,
- radius: clonedObjectRadius
- });
- segmentParentEl.append(lampsParentEl);
+ segmentParentEl.setAttribute(
+ 'street-generated-fixed',
+ `model: lamp-traditional; length: ${length};`
+ );
} else if (segments[i].type === 'transit-shelter') {
var rotationBusStopY = variantList[0] === 'left' ? 90 : 270;
- segmentParentEl.append(
- createBusStopElement(rotationBusStopY, elevationPosY)
+ segmentParentEl.setAttribute(
+ 'street-generated-single',
+ `model: bus-stop; length: ${length}; facing: ${rotationBusStopY};`
);
} else if (segments[i].type === 'brt-station') {
- segmentParentEl.append(createBrtStationElement());
+ segmentParentEl.setAttribute(
+ 'street-generated-single',
+ `model: brt-station; length: ${length};`
+ );
} else if (
segments[i].type === 'separator' &&
variantList[0] === 'dashed'
) {
- groundMixinId = 'markings dashed-stripe';
- positionY = elevationPosY + 0.01; // make sure the lane marker is above the asphalt
+ segmentPreset = 'dashed-stripe';
+ positionY = 0.01; // make sure the lane marker is above the asphalt
// for all markings material property repeat = "1 25". So every 150/25=6 meters put a dash
repeatCount[0] = 1;
repeatCount[1] = parseInt(length / 6);
} else if (segments[i].type === 'separator' && variantList[0] === 'solid') {
- groundMixinId = 'markings solid-stripe';
- positionY = elevationPosY + 0.01; // make sure the lane marker is above the asphalt
+ segmentPreset = 'solid-stripe';
+ positionY = 0.01; // make sure the lane marker is above the asphalt
} else if (
segments[i].type === 'separator' &&
variantList[0] === 'doubleyellow'
) {
- groundMixinId = 'markings solid-doubleyellow';
- positionY = elevationPosY + 0.01; // make sure the lane marker is above the asphalt
+ segmentPreset = 'solid-doubleyellow';
+ positionY = 0.01; // make sure the lane marker is above the asphalt
} else if (
segments[i].type === 'separator' &&
variantList[0] === 'shortdashedyellow'
) {
- groundMixinId = 'markings yellow short-dashed-stripe';
- positionY = elevationPosY + 0.01; // make sure the lane marker is above the asphalt
+ segmentPreset = 'short-dashed-stripe-yellow';
+ positionY = 0.01; // make sure the lane marker is above the asphalt
// for short-dashed-stripe every 3 meters put a dash
repeatCount[0] = 1;
repeatCount[1] = parseInt(length / 3);
@@ -1576,21 +1238,21 @@ function processSegments(
segments[i].type === 'separator' &&
variantList[0] === 'soliddashedyellow'
) {
- groundMixinId = 'markings yellow solid-dashed';
- positionY = elevationPosY + 0.01; // make sure the lane marker is above the asphalt
+ segmentPreset = 'solid-dashed-yellow';
+ positionY = 0.01; // make sure the lane marker is above the asphalt
} else if (
segments[i].type === 'separator' &&
variantList[0] === 'soliddashedyellowinverted'
) {
- groundMixinId = 'markings yellow solid-dashed';
- positionY = elevationPosY + 0.01; // make sure the lane marker is above the asphalt
+ segmentPreset = 'solid-dashed-yellow';
+ positionY = 0.01; // make sure the lane marker is above the asphalt
rotationY = '180';
repeatCount[0] = 1;
repeatCount[1] = parseInt(length / 6);
} else if (segments[i].type === 'parking-lane') {
let reusableObjectStencilsParentEl;
- groundMixinId = 'bright-lane';
+ segmentPreset = 'parking-lane';
let parkingMixin = 'stencils parking-t';
const carCount = 5;
@@ -1621,7 +1283,7 @@ function processSegments(
carStep = 3;
markingLength = segmentWidthInMeters;
markingPosX = 0;
- parkingMixin = 'markings solid-stripe';
+ parkingMixin = 'solid-stripe';
}
const markingPosXY = markingPosX + ' 0';
const clonedStencilRadius = length / 2 - carStep;
@@ -1639,7 +1301,7 @@ function processSegments(
);
if (variantList[1] === 'left') {
reusableObjectStencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015
+ y: 0.015
});
cloneMixinAsChildren({
objectMixinId: parkingMixin,
@@ -1652,7 +1314,7 @@ function processSegments(
});
} else {
reusableObjectStencilsParentEl = createStencilsParentElement({
- y: elevationPosY + 0.015
+ y: 0.015
});
cloneMixinAsChildren({
objectMixinId: parkingMixin,
@@ -1668,31 +1330,36 @@ function processSegments(
segmentParentEl.append(reusableObjectStencilsParentEl);
}
+ // if this thing is a sidewalk, make segmentPreset sidewalk
if (streetmixParsersTested.isSidewalk(segments[i].type)) {
- groundMixinId = 'sidewalk';
- repeatCount[0] = segmentWidthInMeters / 1.5;
- // every 2 meters repeat sidewalk texture
- repeatCount[1] = parseInt(length / 2);
+ segmentPreset = 'sidewalk';
}
-
// add new object
if (segments[i].type !== 'separator') {
- segmentParentEl.append(
- createSegmentElement(
- segmentWidthInMeters,
- positionY,
- groundMixinId,
- length,
- repeatCount,
- elevation
- )
+ segmentParentEl.setAttribute('street-segment', 'type', segmentPreset);
+ segmentParentEl.setAttribute(
+ 'street-segment',
+ 'width',
+ segmentWidthInMeters
+ );
+ segmentParentEl.setAttribute('street-segment', 'length', length);
+ segmentParentEl.setAttribute('street-segment', 'elevation', elevation);
+ segmentParentEl.setAttribute(
+ 'street-segment',
+ 'color',
+ segmentColor ?? TYPES[segmentPreset]?.color
+ );
+ segmentParentEl.setAttribute(
+ 'street-segment',
+ 'surface',
+ TYPES[segmentPreset]?.surface
);
} else {
segmentParentEl.append(
createSeparatorElement(
positionY,
rotationY,
- groundMixinId,
+ segmentPreset,
length,
repeatCount,
elevation
@@ -1711,11 +1378,11 @@ function processSegments(
// create new brown box to represent ground underneath street
const dirtBox = document.createElement('a-box');
const xPos = cumulativeWidthInMeters / 2;
- dirtBox.setAttribute('position', `${xPos} -1.1 0`); // what is x? x = 0 - cumulativeWidthInMeters / 2
+ dirtBox.setAttribute('position', `${xPos} -1 0`); // what is x? x = 0 - cumulativeWidthInMeters / 2
dirtBox.setAttribute('height', 2); // height is 2 meters from y of -0.1 to -y of 2.1
dirtBox.setAttribute('width', cumulativeWidthInMeters);
dirtBox.setAttribute('depth', length - 0.2); // depth is length - 0.1 on each side
- dirtBox.setAttribute('material', 'color: #664B00;');
+ dirtBox.setAttribute('material', `color: ${COLORS.brown};`);
dirtBox.setAttribute('data-layer-name', 'Underground');
streetParentEl.append(dirtBox);
return streetParentEl;
@@ -1725,7 +1392,6 @@ module.exports.processSegments = processSegments;
// test - for streetObject of street 44 and buildingElementId render 2 building sides
function processBuildings(left, right, streetWidth, showGround, length) {
const buildingElement = document.createElement('a-entity');
- const clonedObjectRadius = 0.45 * length;
buildingElement.classList.add('buildings-parent');
buildingElement.setAttribute(
'data-layer-name',
@@ -1856,7 +1522,6 @@ function processBuildings(left, right, streetWidth, showGround, length) {
if (currentValue === 'waterfront' || currentValue === 'compound-wall') {
const objectPositionX = buildingPositionX - (sideMultiplier * 150) / 2;
const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'seawall-parent');
placedObjectEl.setAttribute('position', { x: objectPositionX, z: 4.5 }); // position="1.043 0.100 -3.463"
let rotationCloneY;
if (currentValue === 'compound-wall') {
@@ -1868,34 +1533,26 @@ function processBuildings(left, right, streetWidth, showGround, length) {
} else {
rotationCloneY = side === 'left' ? -90 : 90;
}
- placedObjectEl.classList.add('seawall-parent-' + side);
+ placedObjectEl.setAttribute('data-layer-name', 'seawall-parent-' + side);
+ placedObjectEl.setAttribute(
+ 'street-generated-fixed',
+ `model: seawall; length: ${length}; facing: ${rotationCloneY}; cycleOffset: 0.8;`
+ );
buildingElement.appendChild(placedObjectEl);
- // clone a bunch of seawalls under the parent
- cloneMixinAsChildren({
- objectMixinId: 'seawall',
- parentEl: placedObjectEl,
- rotation: '0 ' + rotationCloneY + ' 0',
- step: 15,
- radius: clonedObjectRadius
- });
}
if (currentValue === 'fence' || currentValue === 'parking-lot') {
const objectPositionX = buildingPositionX - (sideMultiplier * 150) / 2;
// make the parent for all the objects to be cloned
const placedObjectEl = document.createElement('a-entity');
- placedObjectEl.setAttribute('class', 'fence-parent');
placedObjectEl.setAttribute('position', objectPositionX + ' 0 4.625'); // position="1.043 0.100 -3.463"
- placedObjectEl.classList.add('fence-parent-' + buildingPositionX);
+ placedObjectEl.setAttribute('data-layer-name', 'fence-parent');
// clone a bunch of fences under the parent
const rotationCloneY = side === 'right' ? -90 : 90;
- cloneMixinAsChildren({
- objectMixinId: 'fence',
- parentEl: placedObjectEl,
- rotation: '0 ' + rotationCloneY + ' 0',
- step: 9.25,
- radius: clonedObjectRadius
- });
+ placedObjectEl.setAttribute(
+ 'street-generated-fixed',
+ `model: fence; length: ${length}; spacing: 9.25; facing: ${rotationCloneY}; cycleOffset: 1`
+ );
buildingElement.appendChild(placedObjectEl);
}
});
diff --git a/src/assets.js b/src/assets.js
index e7f666f2..69f48013 100644
--- a/src/assets.js
+++ b/src/assets.js
@@ -128,63 +128,64 @@ function buildAssetHTML(assetUrl, categories) {
-
+
`,
'segment-colors': `
-
`,
'lane-separator': `
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
`,
stencils: `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`,
'vehicles-transit': `
-
-
+
+
`,
@@ -229,7 +230,7 @@ function buildAssetHTML(assetUrl, categories) {
-
+
@@ -383,7 +384,7 @@ Unused assets kept commented here for future reference
-
+
diff --git a/src/components/street-generated-fixed.js b/src/components/street-generated-fixed.js
new file mode 100644
index 00000000..6c040141
--- /dev/null
+++ b/src/components/street-generated-fixed.js
@@ -0,0 +1,107 @@
+/* global AFRAME */
+
+// a-frame component to generate cloned models along a street
+// this moves logic from aframe-streetmix-parsers into this component
+
+AFRAME.registerComponent('street-generated-fixed', {
+ multiple: true,
+ schema: {
+ model: {
+ type: 'string'
+ },
+ length: {
+ // length in meters of linear path to fill with clones
+ type: 'number'
+ },
+ spacing: {
+ // spacing in meters between clones
+ default: 15,
+ type: 'number'
+ },
+ positionX: {
+ // x position of clones along the length
+ default: 0,
+ type: 'number'
+ },
+ positionY: {
+ // y position of clones along the length
+ default: 0,
+ type: 'number'
+ },
+ cycleOffset: {
+ // z (inbound/outbound) offset as a fraction of spacing value
+ default: 0.5, // this is used to place different models at different z-levels with the same spacing value
+ type: 'number'
+ },
+ facing: {
+ default: 0, // this is a Y Rotation value in degrees -- UI could offer a dropdown with options for 0, 90, 180, 270
+ type: 'number'
+ },
+ randomFacing: {
+ // if true, facing is ignored and a random Y Rotation is applied to each clone
+ default: false,
+ type: 'boolean'
+ },
+ rotationX: {
+ default: 0,
+ type: 'number'
+ }
+ // seed: { // seed not yet supported
+ // default: 0,
+ // type: 'number'
+ // }
+ },
+ init: function () {
+ this.createdEntities = [];
+ },
+ update: function (oldData) {
+ // generate a function that creates a cloned set of x entities based on spacing and length values from the model shortname gltf file loaded in aframe
+ const data = this.data;
+ // if oldData is same as current data, then don't update
+ if (AFRAME.utils.deepEqual(oldData, data)) {
+ return;
+ }
+
+ // For each clone in this.entities, remove it
+ this.createdEntities.forEach((entity) => {
+ entity.remove();
+ });
+ this.createdEntities = [];
+
+ this.correctedSpacing = data.spacing < 1 ? 1 : data.spacing; // return 1 if data.spacing is less than 1
+
+ // Calculate number of clones needed based on length and spacing
+ const numClones = Math.floor(data.length / this.correctedSpacing);
+
+ // Create clones and position them along the length
+ for (let i = 0; i < numClones; i++) {
+ const clone = document.createElement('a-entity');
+ clone.setAttribute('mixin', data.model);
+ // Position each clone evenly spaced along z-axis
+ // offset default is 0.5 so that clones don't start exactly at street start which looks weird
+ const positionZ =
+ data.length / 2 - (i + data.cycleOffset) * this.correctedSpacing;
+ clone.setAttribute('position', {
+ x: data.positionX,
+ y: data.positionY,
+ z: positionZ
+ });
+
+ if (data.randomFacing) {
+ clone.setAttribute(
+ 'rotation',
+ `${data.rotationX} ${Math.random() * 360} 0`
+ );
+ } else {
+ clone.setAttribute('rotation', `${data.rotationX} ${data.facing} 0`);
+ }
+ clone.classList.add('autocreated');
+ // clone.setAttribute('data-ignore-raycaster', ''); // i still like clicking to zoom to individual clones, but instead this should show the generated-fixed clone settings
+ clone.setAttribute('data-no-transform', '');
+ clone.setAttribute('data-layer-name', 'Cloned Model • ' + data.model);
+
+ this.el.appendChild(clone);
+ this.createdEntities.push(clone);
+ }
+ }
+});
diff --git a/src/components/street-generated-random.js b/src/components/street-generated-random.js
new file mode 100644
index 00000000..8792ec77
--- /dev/null
+++ b/src/components/street-generated-random.js
@@ -0,0 +1,127 @@
+/* global AFRAME */
+
+// a-frame component to generate cloned models along a street with random z position
+// this moves logic from aframe-streetmix-parsers into this component
+AFRAME.registerComponent('street-generated-random', {
+ multiple: true,
+ schema: {
+ model: {
+ type: 'string'
+ },
+ modelsArray: {
+ type: 'array'
+ },
+ length: {
+ // length in meters of linear path to fill with clones
+ type: 'number'
+ },
+ count: {
+ // number of clones to create with random z
+ default: 1,
+ type: 'number'
+ },
+ placeLength: {
+ // length of the place for each model in meters
+ default: 1,
+ type: 'number'
+ },
+ positionX: {
+ // x position of clones along the length
+ default: 0,
+ type: 'number'
+ },
+ positionY: {
+ // y position of clones along the length
+ default: 0,
+ type: 'number'
+ },
+ facing: {
+ default: 0, // this is a Y Rotation value in degrees -- UI could offer a dropdown with options for 0, 90, 180, 270
+ type: 'number'
+ },
+ randomFacing: {
+ // if true, facing is ignored and a random Y Rotation is applied to each clone
+ default: false,
+ type: 'boolean'
+ }
+ // seed: { // seed not yet supported
+ // default: 0,
+ // type: 'number'
+ // }
+ },
+ init: function () {
+ this.createdEntities = [];
+ },
+ update: function (oldData) {
+ // generate a function that creates a cloned set of x entities based on spacing and length values from the model shortname gltf file loaded in aframe
+ const data = this.data;
+ // if oldData is same as current data, then don't update
+ if (AFRAME.utils.deepEqual(oldData, data)) {
+ return;
+ }
+
+ // For each clone in this.entities, remove it
+ this.createdEntities.forEach((entity) => {
+ entity.remove();
+ });
+ this.createdEntities = [];
+
+ // Calculate number of places needed based on length and objLength
+ const randPlaces = this.randPlacedElements(
+ data.length,
+ data.placeLength,
+ data.count
+ );
+
+ // Create clones
+ randPlaces.forEach((randPosZ) => {
+ const clone = document.createElement('a-entity');
+ clone.setAttribute('mixin', this.getRandomMixin());
+ clone.setAttribute('position', {
+ x: data.positionX,
+ y: data.positionY,
+ z: randPosZ
+ });
+ if (data.randomFacing) {
+ clone.setAttribute('rotation', `0 ${Math.random() * 360} 0`);
+ } else {
+ clone.setAttribute('rotation', `0 ${data.facing} 0`);
+ }
+ clone.classList.add('autocreated');
+ // clone.setAttribute('data-ignore-raycaster', ''); // i still like clicking to zoom to individual clones, but instead this should show the generated-fixed clone settings
+ clone.setAttribute('data-no-transform', '');
+ clone.setAttribute('data-layer-name', 'Cloned Model • ' + data.model);
+
+ this.el.appendChild(clone);
+ this.createdEntities.push(clone);
+ });
+ },
+ getRandomMixin: function () {
+ const data = this.data;
+ if (data.modelsArray && data.modelsArray.length > 0) {
+ return data.modelsArray[
+ Math.floor(Math.random() * data.modelsArray.length)
+ ];
+ }
+ return data.model;
+ },
+ randPlacedElements: function (streetLength, placeLength, count) {
+ // Calculate start and end positions
+ const start = -streetLength / 2 + placeLength / 2;
+ const end = streetLength / 2 - placeLength / 2;
+
+ // Calculate number of possible positions
+ const len = Math.floor((end - start) / placeLength) + 1;
+
+ // Generate array of evenly spaced positions
+ const positions = Array(len)
+ .fill()
+ .map((_, idx) => start + idx * placeLength);
+
+ // Randomly shuffle positions
+ const shuffledPositions = positions.sort(() => 0.5 - Math.random());
+
+ // Return only requested number of positions
+ return shuffledPositions.slice(0, count);
+ }
+});
diff --git a/src/components/street-generated-single.js b/src/components/street-generated-single.js
new file mode 100644
index 00000000..71cc186c
--- /dev/null
+++ b/src/components/street-generated-single.js
@@ -0,0 +1,90 @@
+/* global AFRAME */
+
+// a-frame component to one cloned model along a street
+// this moves logic from aframe-streetmix-parsers into this component
+
+AFRAME.registerComponent('street-generated-single', {
+ multiple: true,
+ schema: {
+ model: {
+ type: 'string'
+ },
+ length: {
+ // length in meters of segment
+ type: 'number'
+ },
+ justify: {
+ default: 'middle',
+ oneOf: ['start', 'middle', 'end']
+ },
+ padding: {
+ // spacing in meters between segment edge and model
+ default: 4,
+ type: 'number'
+ },
+ positionX: {
+ // x position of model
+ default: 0,
+ type: 'number'
+ },
+ positionY: {
+ // y position of model
+ default: 0,
+ type: 'number'
+ },
+ facing: {
+ default: 0, // this is a Y Rotation value in degrees -- UI could offer a dropdown with options for 0, 90, 180, 270
+ type: 'number'
+ },
+ randomFacing: {
+ // if true, facing is ignored and a random Y Rotation is applied to each clone
+ default: false,
+ type: 'boolean'
+ }
+ },
+ init: function () {
+ this.createdEntities = [];
+ },
+ update: function (oldData) {
+ const data = this.data;
+ // if oldData is same as current data, then don't update
+ if (AFRAME.utils.deepEqual(oldData, data)) {
+ return;
+ }
+
+ // For each clone in this.entities, remove it
+ this.createdEntities.forEach((entity) => {
+ entity.remove();
+ });
+ this.createdEntities = [];
+
+ const clone = document.createElement('a-entity');
+ clone.setAttribute('mixin', data.model);
+
+ // Position z is dependent upon length and padding
+ let positionZ = 0; // middle
+ if (data.justify === 'start') {
+ positionZ = data.length / 2 - data.padding;
+ } else if (data.justify === 'end') {
+ positionZ = -data.length / 2 + data.padding;
+ }
+
+ clone.setAttribute('position', {
+ x: data.positionX,
+ y: data.positionY,
+ z: positionZ
+ });
+
+ if (data.randomFacing) {
+ clone.setAttribute('rotation', `0 ${Math.random() * 360} 0`);
+ } else {
+ clone.setAttribute('rotation', `0 ${data.facing} 0`);
+ }
+ clone.classList.add('autocreated');
+ // clone.setAttribute('data-ignore-raycaster', ''); // i still like clicking to zoom to individual clones, but instead this should show the generated-fixed clone settings
+ clone.setAttribute('data-no-transform', '');
+ clone.setAttribute('data-layer-name', 'Cloned Model • ' + data.model);
+ this.el.appendChild(clone);
+ this.createdEntities.push(clone);
+ }
+});
diff --git a/src/components/street-segment.js b/src/components/street-segment.js
new file mode 100644
index 00000000..288d12ce
--- /dev/null
+++ b/src/components/street-segment.js
@@ -0,0 +1,177 @@
+/* global AFRAME */
+
+/*
+
+
+
+
+
+
+*/
+
+AFRAME.registerGeometry('below-box', {
+ schema: {
+ depth: { default: 1, min: 0 },
+ height: { default: 1, min: 0 },
+ width: { default: 1, min: 0 },
+ segmentsHeight: { default: 1, min: 1, max: 20, type: 'int' },
+ segmentsWidth: { default: 1, min: 1, max: 20, type: 'int' },
+ segmentsDepth: { default: 1, min: 1, max: 20, type: 'int' }
+ },
+
+ init: function (data) {
+ this.geometry = new THREE.BoxGeometry(
+ data.width,
+ data.height,
+ data.depth,
+ data.segmentsWidth,
+ data.segmentsHeight,
+ data.segmentsDepth
+ );
+ this.geometry.translate(0, -data.height / 2, 0);
+ }
+});
+
+AFRAME.registerComponent('street-segment', {
+ schema: {
+ width: {
+ type: 'number'
+ },
+ length: {
+ type: 'number'
+ },
+ elevation: {
+ type: 'int',
+ default: 0
+ },
+ direction: {
+ type: 'string',
+ oneOf: ['inbound', 'outbound']
+ },
+ surface: {
+ type: 'string',
+ default: 'asphalt',
+ oneOf: [
+ 'asphalt',
+ 'concrete',
+ 'grass',
+ 'sidewalk',
+ 'gravel',
+ 'sand',
+ 'none',
+ 'solid'
+ ]
+ },
+ color: {
+ type: 'color'
+ }
+ },
+ init: function () {
+ this.height = 0.2; // default height of segment surface box
+ },
+ update: function (oldData) {
+ const data = this.data;
+ // if oldData is same as current data, then don't update
+ if (AFRAME.utils.deepEqual(oldData, data)) {
+ return;
+ }
+ this.clearMesh();
+ this.height = this.calculateHeight(data.elevation);
+ this.tempXPosition = this.el.getAttribute('position').x;
+ this.el.setAttribute('position', { x: this.tempXPosition, y: this.height });
+ this.generateMesh(data);
+ },
+ // for streetmix elevation number values of -1, 0, 1, 2, calculate heightLevel in three.js meters units
+ calculateHeight: function (elevation) {
+ const stepLevel = 0.15;
+ if (elevation <= 0) {
+ return stepLevel;
+ }
+ return stepLevel * (elevation + 1);
+ },
+ clearMesh: function () {
+ // remove the geometry from the entity
+ this.el.removeAttribute('geometry');
+ this.el.removeAttribute('material');
+ },
+ remove: function () {
+ this.clearMesh();
+ },
+ generateMesh: function (data) {
+ // create geometry
+ this.el.setAttribute(
+ 'geometry',
+ `primitive: below-box;
+ height: ${this.height};
+ depth: ${data.length};
+ width: ${data.width};`
+ );
+
+ // create a lookup table to convert UI shortname into A-Frame img id's
+ const textureMaps = {
+ asphalt: 'seamless-road',
+ concrete: 'seamless-bright-road',
+ grass: 'grass-texture',
+ sidewalk: 'seamless-sidewalk',
+ gravel: 'compacted-gravel-texture',
+ sand: 'sandy-asphalt-texture',
+ hatched: 'hatched-base',
+ none: 'none',
+ solid: ''
+ };
+ let textureSourceId = textureMaps[data.surface];
+
+ // calculate the repeatCount for the material
+ let [repeatX, repeatY, offsetX] = this.calculateTextureRepeat(
+ data.length,
+ data.width,
+ textureSourceId
+ );
+
+ this.el.setAttribute(
+ 'material',
+ `src: #${textureMaps[data.surface]};
+ roughness: 0.8;
+ repeat: ${repeatX} ${repeatY};
+ offset: ${offsetX} 0;
+ color: ${data.color}`
+ );
+
+ this.el.setAttribute('shadow', '');
+
+ this.el.setAttribute(
+ 'material',
+ 'visible',
+ textureMaps[data.surface] !== 'none'
+ );
+
+ return;
+ },
+ calculateTextureRepeat: function (length, width, textureSourceId) {
+ // calculate the repeatCount for the material
+ let repeatX = 0.3; // drive-lane, bus-lane, bike-lane
+ let repeatY = length / 6;
+ let offsetX = 0.55; // we could get rid of this using cropped texture for asphalt
+ if (textureSourceId === 'seamless-bright-road') {
+ repeatX = 0.6;
+ repeatY = 15;
+ } else if (textureSourceId === 'seamless-sandy-road') {
+ repeatX = width / 30;
+ repeatY = length / 30;
+ offsetX = 0;
+ } else if (textureSourceId === 'seamless-sidewalk') {
+ repeatX = width / 2;
+ repeatY = length / 2;
+ offsetX = 0;
+ } else if (textureSourceId === 'grass-texture') {
+ repeatX = width / 4;
+ repeatY = length / 6;
+ offsetX = 0;
+ } else if (textureSourceId === 'hatched-base') {
+ repeatX = 1;
+ repeatY = length / 4;
+ offsetX = 0;
+ }
+ return [repeatX, repeatY, offsetX];
+ }
+});
diff --git a/src/index.js b/src/index.js
index b8817c5e..311191f2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -21,6 +21,10 @@ require('./components/street-geo.js');
require('./components/street-environment.js');
require('./components/intersection.js');
require('./components/obb-clipping.js');
+require('./components/street-segment.js');
+require('./components/street-generated-fixed.js');
+require('./components/street-generated-single.js');
+require('./components/street-generated-random.js');
require('./editor/index.js');
const state = useStore.getState();