Use D3 to create a contour/polar graph - d3.js
Using D3, I want to create a polar graph like this figure.
Following the volcano example, I can get the contour correctly rendered.
How do I translate the contours onto a circle to create a polar graph?
Update:
The initial data I'm using is a 2d array, where the first dimension (array[n]) represents period and the second dimension (array[n][0]) represents force. See below for a sample of data and the code I'm using to draw the contour graph.
function draw(spectrumData: Swell["spectrum"]): void {
if (!spectrumData) {
console.warn("spectrumData is undefined");
return;
}
const { spectrum, width, height } = transformSpectrum(spectrumData);
const sizeFactor = 13;
const svg = d3
.select("#spectrum-contour")
.attr("width", width * sizeFactor)
.attr("height", height * sizeFactor);
// Ret the contour size
var contour = d3Contour.contours().size([width, height]);
// Returns "rgb(x, y, z)"
const interpolator = (t: number): string => {
const i0 = d3Hsv.interpolateHsvLong(
d3Hsv.hsv(240, 0.77, 0.73),
d3Hsv.hsv(114, 0.77, 0.73)
);
const i1 = d3Hsv.interpolateHsvLong(
d3Hsv.hsv(114, 0.77, 0.73),
d3Hsv.hsv(0, 0.77, 0.73)
);
return t < 0.2 ? i0(t * 2) : i1((t - 0.2) * 2);
};
const colorScale = d3Scale
.scaleSequential(interpolator)
// extent returns [min, max]
.domain(d3.extent(spectrum));
const path: any = d3Geo.geoPath(
d3Geo.geoIdentity().scale((width * sizeFactor) / width)
);
// draw the contours
svg
.selectAll("path")
.data(contour(spectrum))
.enter()
.append("path")
.attr("d", (feature) => path(feature))
.attr("fill", (d) => colorScale(d.value));
}
// Transform the 2D spectrum array to a 1D array
// and replace negative values with zeros.
function transformSpectrum(data: Swell["spectrum"]): TransformedSpectrum {
const array1d = ([] as number[]).concat.apply([], data);
return {
width: data[0].length,
height: data.length,
spectrum: array1d.map((i) => (i >= 0 ? i : 0)),
};
}
"spectrum": [
[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ],
[ 0.0002, 0.0003, 0.0003, 0.0003, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0003, 0.0003, 0.0003, 0.0003, 0.0002, 0.0002, 0.0002, 0.0002, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0002, 0.0002, 0.0002, 0.0003, 0.0003, 0.0003, 0.0004, 0.0004, 0.0005, 0.0005, 0.0006, 0.0006, 0.0007, 0.0007, 0.0007 ],
[ 0.0001, 0.0001, 0.0002, 0.0002, 0.0003, 0.0003, 0.0004, 0.0004, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0005, 0.0005, 0.0005, 0.0006, 0.0006, 0.0007, 0.0007, 0.0007, 0.0008, 0.0008 ],
[ 0.0003, 0.0003, 0.0002, 0.0002, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0002, 0.0002, 0.0003, 0.0004, 0.0005, 0.0006, 0.0007, 0.0009, 0.001, 0.0011, 0.0013, 0.0014, 0.0015, 0.0016, 0.0017, 0.0018, 0.0018, 0.0018, 0.0019, 0.0018, 0.0018, 0.0018, 0.0017, 0.0016, 0.0015, 0.0014, 0.0013, 0.0011, 0.001, 0.0008, 0.0007 ],
[ 0.0007, 0.0007, 0.0006, 0.0006, 0.0006, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0006, 0.0006, 0.0007, 0.0008, 0.0009, 0.001, 0.0011, 0.0012, 0.0013, 0.0015, 0.0016, 0.0016, 0.0017, 0.0018, 0.0018, 0.0019, 0.0019, 0.0018, 0.0018, 0.0018, 0.0017, 0.0016, 0.0015, 0.0014, 0.0012, 0.0011, 0.001, 0.0008 ],
[ 0.0007, 0.0006, 0.0005, 0.0004, 0.0004, 0.0003, 0.0002, 0.0002, 0.0002, 0.0002, 0.0002, 0.0002, 0.0003, 0.0004, 0.0005, 0.0006, 0.0007, 0.0008, 0.001, 0.0012, 0.0013, 0.0015, 0.0017, 0.0018, 0.002, 0.0021, 0.0022, 0.0023, 0.0023, 0.0023, 0.0024, 0.0023, 0.0023, 0.0022, 0.0021, 0.002, 0.0019, 0.0017, 0.0016, 0.0014 ],
[ 0.0001, 0.0001, 0.0002, 0.0002, 0.0003, 0.0004, 0.0006, 0.0007, 0.0009, 0.0011, 0.0013, 0.0016, 0.0018, 0.0021, 0.0023, 0.0026, 0.0029, 0.0032, 0.0034, 0.0037, 0.0039, 0.0041, 0.0042, 0.0044, 0.0045, 0.0045, 0.0046, 0.0046, 0.0045, 0.0044, 0.0043, 0.0042, 0.004, 0.0038, 0.0036, 0.0034, 0.0031, 0.0029, 0.0026, 0.0024 ],
[ 0.0014, 0.001, 0.0006, 0.0003, 0.0, -0.0002, -0.0003, -0.0003, -0.0001, 0.0003, 0.0008, 0.0015, 0.0024, 0.0035, 0.0047, 0.0061, 0.0076, 0.0092, 0.0109, 0.0126, 0.0143, 0.016, 0.0175, 0.019, 0.0203, 0.0214, 0.0224, 0.023, 0.0235, 0.0236, 0.0235, 0.0231, 0.0224, 0.0215, 0.0204, 0.019, 0.0175, 0.0158, 0.014, 0.0122 ],
[ 0.0036, 0.0023, 0.0009, -0.0005, -0.0019, -0.0033, -0.0045, -0.0054, -0.006, -0.0062, -0.0061, -0.0054, -0.0043, -0.0027, -0.0005, 0.0021, 0.0052, 0.0086, 0.0124, 0.0165, 0.0207, 0.0251, 0.0294, 0.0336, 0.0376, 0.0413, 0.0447, 0.0475, 0.0498, 0.0516, 0.0526, 0.0531, 0.0528, 0.0519, 0.0503, 0.0481, 0.0453, 0.0421, 0.0385, 0.0345 ],
[ 0.0031, 0.0022, 0.0013, 0.0003, -0.0008, -0.0018, -0.0027, -0.0035, -0.0041, -0.0045, -0.0046, -0.0044, -0.0039, -0.0029, -0.0016, 0.0, 0.002, 0.0043, 0.0069, 0.0098, 0.0128, 0.0159, 0.0191, 0.0222, 0.0253, 0.0282, 0.0309, 0.0333, 0.0353, 0.0369, 0.038, 0.0387, 0.0389, 0.0386, 0.0378, 0.0366, 0.0349, 0.0328, 0.0304, 0.0277 ],
[ 0.001, -0.0001, -0.0012, -0.0022, -0.0032, -0.0039, -0.0045, -0.0047, -0.0047, -0.0044, -0.0038, -0.0027, -0.0014, 0.0003, 0.0022, 0.0044, 0.0069, 0.0095, 0.0122, 0.015, 0.0177, 0.0204, 0.0229, 0.0252, 0.0272, 0.0289, 0.0302, 0.0311, 0.0316, 0.0316, 0.0312, 0.0303, 0.029, 0.0274, 0.0254, 0.0231, 0.0205, 0.0178, 0.015, 0.0121 ],
[ -0.0002, -0.0006, -0.001, -0.0014, -0.0017, -0.0019, -0.002, -0.0019, -0.0018, -0.0015, -0.0011, -0.0005, 0.0001, 0.0009, 0.0019, 0.0029, 0.0039, 0.0051, 0.0062, 0.0073, 0.0085, 0.0095, 0.0105, 0.0114, 0.0121, 0.0127, 0.0131, 0.0134, 0.0135, 0.0134, 0.0131, 0.0126, 0.012, 0.0113, 0.0104, 0.0094, 0.0084, 0.0072, 0.0061, 0.005 ],
[ 0.0003, 0.0, -0.0004, -0.0007, -0.001, -0.0013, -0.0015, -0.0016, -0.0016, -0.0015, -0.0012, -0.0009, -0.0004, 0.0002, 0.0009, 0.0017, 0.0026, 0.0035, 0.0045, 0.0056, 0.0067, 0.0077, 0.0087, 0.0096, 0.0105, 0.0112, 0.0119, 0.0123, 0.0127, 0.0128, 0.0128, 0.0127, 0.0124, 0.0119, 0.0113, 0.0105, 0.0097, 0.0088, 0.0078, 0.0067 ],
[ -0.0003, -0.0004, -0.0005, -0.0005, -0.0005, -0.0005, -0.0004, -0.0003, -0.0002, 0.0, 0.0002, 0.0005, 0.0008, 0.0011, 0.0014, 0.0018, 0.0021, 0.0025, 0.0028, 0.0032, 0.0035, 0.0038, 0.004, 0.0042, 0.0044, 0.0045, 0.0046, 0.0046, 0.0046, 0.0045, 0.0044, 0.0042, 0.004, 0.0037, 0.0035, 0.0032, 0.0028, 0.0025, 0.0022, 0.0019 ],
[ -0.0004, -0.0006, -0.0008, -0.0009, -0.001, -0.0011, -0.0011, -0.001, -0.0009, -0.0007, -0.0004, -0.0001, 0.0003, 0.0008, 0.0013, 0.0019, 0.0024, 0.003, 0.0036, 0.0042, 0.0048, 0.0053, 0.0057, 0.0062, 0.0065, 0.0067, 0.0069, 0.007, 0.007, 0.0069, 0.0067, 0.0064, 0.0061, 0.0057, 0.0052, 0.0047, 0.0041, 0.0036, 0.003, 0.0024 ],
[ -0.0001, -0.0002, -0.0004, -0.0005, -0.0006, -0.0007, -0.0007, -0.0007, -0.0007, -0.0006, -0.0005, -0.0003, -0.0001, 0.0002, 0.0005, 0.0009, 0.0013, 0.0017, 0.0021, 0.0026, 0.003, 0.0034, 0.0038, 0.0042, 0.0045, 0.0048, 0.005, 0.0051, 0.0052, 0.0053, 0.0052, 0.0051, 0.005, 0.0048, 0.0045, 0.0042, 0.0038, 0.0034, 0.003, 0.0026 ],
[ -0.0003, -0.0003, -0.0003, -0.0003, -0.0003, -0.0002, -0.0002, -0.0001, 0.0, 0.0001, 0.0002, 0.0004, 0.0005, 0.0007, 0.0009, 0.0011, 0.0013, 0.0015, 0.0017, 0.0018, 0.002, 0.0021, 0.0023, 0.0024, 0.0025, 0.0025, 0.0025, 0.0026, 0.0025, 0.0025, 0.0024, 0.0023, 0.0022, 0.0021, 0.002, 0.0018, 0.0017, 0.0015, 0.0014, 0.0012 ],
[ -0.0002, -0.0002, -0.0002, -0.0001, -0.0001, -0.0001, 0.0, 0.0, 0.0001, 0.0002, 0.0002, 0.0003, 0.0004, 0.0005, 0.0006, 0.0007, 0.0008, 0.0008, 0.0009, 0.001, 0.0011, 0.0011, 0.0011, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0011, 0.0011, 0.0011, 0.001, 0.001, 0.0009, 0.0008, 0.0008, 0.0007, 0.0006, 0.0006, 0.0005 ],
[ -0.0001, -0.0001, -0.0002, -0.0001, -0.0001, -0.0001, -0.0001, 0.0, 0.0, 0.0001, 0.0002, 0.0002, 0.0003, 0.0004, 0.0005, 0.0006, 0.0007, 0.0008, 0.0009, 0.001, 0.0011, 0.0011, 0.0012, 0.0012, 0.0013, 0.0013, 0.0013, 0.0013, 0.0012, 0.0012, 0.0012, 0.0011, 0.0011, 0.001, 0.0009, 0.0008, 0.0008, 0.0007, 0.0006, 0.0005 ],
[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0001, 0.0001, 0.0002, 0.0002, 0.0003, 0.0003, 0.0004, 0.0005, 0.0006, 0.0006, 0.0007, 0.0008, 0.0009, 0.001, 0.0011, 0.0011, 0.0012, 0.0013, 0.0013, 0.0014, 0.0014, 0.0014, 0.0014, 0.0015, 0.0014, 0.0014, 0.0014, 0.0014, 0.0013, 0.0013, 0.0012, 0.0012, 0.0011, 0.001, 0.0009 ],
[ -0.0002, -0.0002, -0.0002, -0.0002, -0.0002, -0.0001, -0.0001, -0.0001, 0.0, 0.0001, 0.0002, 0.0002, 0.0003, 0.0004, 0.0005, 0.0007, 0.0008, 0.0009, 0.001, 0.0011, 0.0011, 0.0012, 0.0013, 0.0013, 0.0014, 0.0014, 0.0014, 0.0014, 0.0013, 0.0013, 0.0012, 0.0012, 0.0011, 0.001, 0.0009, 0.0008, 0.0007, 0.0006, 0.0005, 0.0005 ],
[ 0.0002, 0.0002, 0.0002, 0.0001, 0.0001, 0.0, 0.0, 0.0, -0.0001, -0.0001, -0.0001, -0.0001, -0.0001, -0.0001, -0.0001, -0.0001, 0.0, 0.0001, 0.0001, 0.0002, 0.0004, 0.0005, 0.0006, 0.0007, 0.0009, 0.001, 0.0011, 0.0012, 0.0013, 0.0014, 0.0015, 0.0015, 0.0016, 0.0016, 0.0016, 0.0016, 0.0015, 0.0015, 0.0014, 0.0013 ],
[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0002, 0.0002, 0.0002, 0.0002, 0.0003, 0.0003, 0.0004, 0.0004, 0.0005, 0.0005, 0.0005, 0.0006, 0.0006, 0.0007, 0.0007, 0.0007, 0.0008, 0.0008, 0.0008, 0.0008, 0.0008, 0.0008, 0.0008, 0.0008, 0.0007, 0.0007 ],
[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0001, 0.0001, 0.0001, 0.0001, 0.0002, 0.0002, 0.0002, 0.0003, 0.0003, 0.0003, 0.0003, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0004, 0.0003, 0.0003, 0.0003, 0.0003, 0.0002 ]
],
Update 2
Using the suggestion below I was able to translate the coordinates with the following:
const path: any = d3Geo.geoPath(
d3Geo.geoTransform({
point: function (x, y) {
const [x2, y2] = polarPoint(x, y);
this.stream.point(x2, y2);
},
})
);
Given your polar plot innerRadius, outerRadius, domainRange (range of arguments), and valueRange (range of values), the polar coordinates of each point (arg, value) are calculated the following way:
const polarPoint = (arg, value) => {
const angle = (arg - domainRange.min) /
(domainRange.max - domainRange.min) * Math.PI * 2;
const radius = (value - valueRange.min) /
(valueRange.max - valueRange.min) * (outerRadius - innerRadius) + innerRadius;
const x = radius * Math.sin(angle);
const y = -radius * Math.cos(angle);
return {x, y};
}
See the snippet as illustration:
const domainRange = {min: 0, max: 100}; // Range of arguments
const valueRange = {min: 0, max: 1000}; // Range of values
const plotWidth = 200; // Width of the cartesian plot
const plotHeight = 100; // Height of the cartesian plot
const innerRadius = 20; // Inner radius of the polar plot
const outerRadius = 100; // Outer radius of the polar plot
const values = [[0, 100],[10,400],[20,700],[30,750],[40,900],[50,600],[60,400],[70,350],[80,300],[90,250],[100,100]]; // pairs of arguments/values
const cartesianPoint = (arg, value) => {
const x = (arg - domainRange.min) / (domainRange.max - domainRange.min) * plotWidth;
const y = -(value - valueRange.min) / (valueRange.max - valueRange.min) * plotHeight;
return {x, y};
};
const polarPoint = (arg, value) => {
const angle = (arg - domainRange.min) / (domainRange.max - domainRange.min) * Math.PI * 2;
const radius = (value - valueRange.min) / (valueRange.max - valueRange.min) * (outerRadius - innerRadius) + innerRadius;
const x = radius * Math.sin(angle);
const y = -radius * Math.cos(angle);
return {x, y};
}
const svg = d3.select('svg');
const cartesianPlot = svg.append('g')
.attr('transform', 'translate(20, 150)');
cartesianPlot.append('rect')
.attr('x', 0)
.attr('y', -plotHeight)
.attr('width', plotWidth)
.attr('height', plotHeight)
.style('fill', '#eee');
let prev = null;
values.forEach(v => {
const point = cartesianPoint(v[0], v[1]);
cartesianPlot.append('circle')
.attr('cx', point.x)
.attr('cy', point.y)
.attr('r', 3)
.style('fill', 'red');
if (prev)
cartesianPlot.append('line')
.attr('x1', prev.x)
.attr('y1', prev.y)
.attr('x2', point.x)
.attr('y2', point.y)
.style('stroke', 'blue')
prev = point;
})
const polarPlot = svg.append('g')
.attr('transform', 'translate(350, 120)');
polarPlot.append('circle')
.attr('r', outerRadius)
.style('fill', '#eee')
polarPlot.append('circle')
.attr('r', innerRadius)
.style('fill', '#fff')
prev = null;
values.forEach(v => {
const point = polarPoint(v[0], v[1]);
polarPlot.append('circle')
.attr('cx', point.x)
.attr('cy', point.y)
.attr('r', 3)
.style('fill', 'red');
if (prev)
polarPlot.append('line')
.attr('x1', prev.x)
.attr('y1', prev.y)
.attr('x2', point.x)
.attr('y2', point.y)
.style('stroke', 'blue')
prev = point;
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="500" height="250" />
Related
Gradient Stop Locations at Variable Values SwiftUI
I'm trying to make a gradient composed of 4 stops in SwiftUI. I'd like the middle two to be anchored to the bottom of a specific stack. Something like this: Imgur Link of what I'm trying to do Here's the gradient code I currently have: LinearGradient(gradient: Gradient(stops: [ //top Gradient.Stop(color: Color(hue: 0.0, saturation: 0.0, brightness: 0.0, opacity: 0.0), location: 0.0), //variable1 Gradient.Stop(color: Color(hue: 0.0, saturation: 0.0, brightness: 0.0, opacity: 0.3), location: 0.30 //<-Variable), //variable2 Gradient.Stop(color: Color(hue: 0.0, saturation: 0.0, brightness: 0.0, opacity: 1.0), location: 0.35 //<-Variable), //bottom Gradient.Stop(color: Color(hue: 0.1, saturation: 1.0, brightness: 0.0, opacity: 1.0), location: 1.0)), startPoint: UnitPoint.top, endPoint: UnitPoint.bottom)
You can simply use the frame size of the "Content Box" to determine at what position to place the gradient stop. I did this by just having a variable called frameHeight. import SwiftUI struct ContentView: View { let screenWidth = UIScreen.main.bounds.width let screenHeight = UIScreen.main.bounds.height var frameHeight = 200 var body: some View { let gradient = Gradient(stops: [Gradient.Stop(color: .blue, location: 0.0), Gradient.Stop(color: .white, location: CGFloat(frameHeight)/screenHeight), Gradient.Stop(color: .black, location: CGFloat(frameHeight)/screenHeight + 0.05), Gradient.Stop(color: .black, location: 1.0)]) VStack{ //Content Box view of frameHeight VStack { Text("Content Box") }.frame(width: screenWidth, height: CGFloat(frameHeight)) Spacer() }.frame(width: screenWidth, height: screenHeight) .background(LinearGradient(gradient: gradient, startPoint: UnitPoint.top, endPoint: UnitPoint.bottom)) .edgesIgnoringSafeArea(.all) } }
SwiftUI - Scale Buttons and tappable area
I have several tappable buttons and two groups. I want that the "Main Hiragana" group is scaled to fit the first two columns and the "Ten-Ten / Maru" group is only scaled to the third column. I have now the following situation: I want to get this situation: I tried scaling with Geometry Reader but it didn't quite work, especially with the tappable area. I have now the following code: import SwiftUI struct testfile: View { var body: some View { VStack { HStack { //Button one Button(action: { print("Test") }){ ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("Main") Text("Hiragana") } .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .foregroundColor(Color("BackgroundInverse")) .scaleEffect(x: 0.8, y: 0.8) } } //Button two Button(action: { print("Test2") }) { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("Ten-Ten") Text("Maru") } .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .foregroundColor(Color("BackgroundInverse")) .scaleEffect(x: 0.8, y: 0.8) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } }.padding() } } How can I change the code to scale the first button and the tappable area to the first two columns below and the second button to only one column.
Adding a GeometryReader as the parent and then setting the width on the said Button will give you this look But be cautious, you'll have to also use the geo to set the width of the buttons below to properly scale on any device. import SwiftUI struct testfile: View { var body: some View { GeometryReader { geo in VStack { HStack { //Button one Group { Button(action: { print("Test") }){ ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("Main") Text("Hiragana") } .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .foregroundColor(Color("BackgroundInverse")) .scaleEffect(x: 0.8, y: 0.8) } } } .frame(width: geo.size.width * 0.625) //Button two Group { Button(action: { print("Test2") }) { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("Ten-Ten") Text("Maru") } .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .foregroundColor(Color("BackgroundInverse")) .scaleEffect(x: 0.8, y: 0.8) } } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } HStack { ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } ZStack { Capsule(style: .continuous) .fill(Color.white) .overlay(Capsule(style: .continuous) .stroke(Color.black, style: StrokeStyle(lineWidth: 2)) ) VStack{ Text("KA") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.05) .scaleEffect(x: 0.8, y: 0.8) .foregroundColor(Color.black) Text("KA-Group") .font(.system(size: 500)) .multilineTextAlignment(.center) .minimumScaleFactor(0.005) .scaleEffect(x: 0.6, y: 0.6) .foregroundColor(Color.black) } } } }.padding() } } } struct test_previews: PreviewProvider { static var previews: some View { testfile() } }
d3 beeswarm chart - modify to pyramid-shaped stacking
I have made considerable progress in replicating a descriptive statistics beeswarm-style visual that can be seen here. I'll also include the picture here for extra convenience: From my code snippet you will see that I have all the trappings of that visual except for the pyramid style stacking. var margins = {top:20, bottom:300, left:30, right:100}; var height = 150; var width = 900; var totalWidth = width+margins.left+margins.right; var totalHeight = height+margins.top+margins.bottom; var svg = d3.select('body') .append('svg') .attr('width', totalWidth) .attr('height', totalHeight); var graphGroup = svg.append('g') .attr('transform', "translate("+margins.left+","+margins.top+")"); var xScale = d3.scaleLinear() .range([0, width]); var data = [{'age': 32.0, 'educ': 12.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 35.0, 'educ': 12.0, 'inlf': 1}, {'age': 34.0, 'educ': 12.0, 'inlf': 1}, {'age': 31, 'educ': 14.0, 'inlf': 1}, {'age': 54.0, 'educ': 12.0, 'inlf': 1}, {'age': 37.0, 'educ': 16.0, 'inlf': 1}, {'age': 54.0, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 12.0, 'inlf': 1}, {'age': 39.0, 'educ': 12.0, 'inlf': 1}, {'age': 33.0, 'educ': 12.0, 'inlf': 1}, {'age': 42.0, 'educ': 11, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 43.0, 'educ': 12.0, 'inlf': 1}, {'age': 43.0, 'educ': 10.0, 'inlf': 1}, {'age': 35.0, 'educ': 11, 'inlf': 1}, {'age': 43.0, 'educ': 12.0, 'inlf': 1}, {'age': 39.0, 'educ': 12.0, 'inlf': 1}, {'age': 45.0, 'educ': 12.0, 'inlf': 1}, {'age': 35.0, 'educ': 12.0, 'inlf': 1}, {'age': 42.0, 'educ': 16.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 13.0, 'inlf': 1}, {'age': 45.0, 'educ': 12.0, 'inlf': 1}, {'age': 31, 'educ': 12.0, 'inlf': 1}, {'age': 43.0, 'educ': 17.0, 'inlf': 1}, {'age': 59.0, 'educ': 12.0, 'inlf': 1}, {'age': 32.0, 'educ': 12.0, 'inlf': 1}, {'age': 31, 'educ': 17.0, 'inlf': 1}, {'age': 42.0, 'educ': 12.0, 'inlf': 1}, {'age': 50.0, 'educ': 11, 'inlf': 1}, {'age': 59.0, 'educ': 16.0, 'inlf': 1}, {'age': 36.0, 'educ': 13.0, 'inlf': 1}, {'age': 51, 'educ': 12.0, 'inlf': 1}, {'age': 45.0, 'educ': 16.0, 'inlf': 1}, {'age': 42.0, 'educ': 11, 'inlf': 1}, {'age': 46.0, 'educ': 12.0, 'inlf': 1}, {'age': 46.0, 'educ': 10.0, 'inlf': 1}, {'age': 51, 'educ': 14.0, 'inlf': 1}, {'age': 30.0, 'educ': 17.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 57.0, 'educ': 12.0, 'inlf': 1}, {'age': 31, 'educ': 16.0, 'inlf': 1}, {'age': 48.0, 'educ': 12.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 34.0, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 16.0, 'inlf': 1}, {'age': 45.0, 'educ': 12.0, 'inlf': 1}, {'age': 51, 'educ': 12.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 46.0, 'educ': 12.0, 'inlf': 1}, {'age': 58.0, 'educ': 12.0, 'inlf': 1}, {'age': 37.0, 'educ': 12.0, 'inlf': 1}, {'age': 52.0, 'educ': 8.0, 'inlf': 1}, {'age': 52.0, 'educ': 10.0, 'inlf': 1}, {'age': 31, 'educ': 16.0, 'inlf': 1}, {'age': 55.0, 'educ': 14.0, 'inlf': 1}, {'age': 34.0, 'educ': 17.0, 'inlf': 1}, {'age': 55.0, 'educ': 14.0, 'inlf': 1}, {'age': 39.0, 'educ': 12.0, 'inlf': 1}, {'age': 40.0, 'educ': 14.0, 'inlf': 1}, {'age': 43.0, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 8.0, 'inlf': 1}, {'age': 47.0, 'educ': 12.0, 'inlf': 1}, {'age': 41, 'educ': 12.0, 'inlf': 1}, {'age': 36.0, 'educ': 8.0, 'inlf': 1}, {'age': 46.0, 'educ': 17.0, 'inlf': 1}, {'age': 34.0, 'educ': 12.0, 'inlf': 1}, {'age': 41, 'educ': 12.0, 'inlf': 1}, {'age': 51, 'educ': 12.0, 'inlf': 1}, {'age': 33.0, 'educ': 12.0, 'inlf': 1}, {'age': 52.0, 'educ': 12.0, 'inlf': 1}, {'age': 58.0, 'educ': 9.0, 'inlf': 1}, {'age': 34.0, 'educ': 10.0, 'inlf': 1}, {'age': 31, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 12.0, 'inlf': 1}, {'age': 32.0, 'educ': 12.0, 'inlf': 1}, {'age': 49.0, 'educ': 17.0, 'inlf': 1}, {'age': 32.0, 'educ': 15.0, 'inlf': 1}, {'age': 58.0, 'educ': 12.0, 'inlf': 1}, {'age': 50.0, 'educ': 6.0, 'inlf': 1}, {'age': 60.0, 'educ': 14.0, 'inlf': 1}, {'age': 50.0, 'educ': 12.0, 'inlf': 1}, {'age': 56.0, 'educ': 14.0, 'inlf': 1}, {'age': 51, 'educ': 9.0, 'inlf': 1}, {'age': 54.0, 'educ': 17.0, 'inlf': 1}, {'age': 59.0, 'educ': 13.0, 'inlf': 1}, {'age': 46.0, 'educ': 9.0, 'inlf': 1}, {'age': 46.0, 'educ': 15.0, 'inlf': 1}, {'age': 39.0, 'educ': 12.0, 'inlf': 0}, {'age': 44.0, 'educ': 12.0, 'inlf': 0}, {'age': 33.0, 'educ': 12.0, 'inlf': 0}, {'age': 33.0, 'educ': 12.0, 'inlf': 0}, {'age': 48.0, 'educ': 12.0, 'inlf': 0}, {'age': 30, 'educ': 12.0, 'inlf': 0}, {'age': 45.0, 'educ': 12.0, 'inlf': 0}, {'age': 45.0, 'educ': 12.0, 'inlf': 0}, {'age': 32.0, 'educ': 13.0, 'inlf': 0}, {'age': 47.0, 'educ': 12.0, 'inlf': 0}, {'age': 34.0, 'educ': 13.0, 'inlf': 0}, {'age': 37.0, 'educ': 12.0, 'inlf': 0}, {'age': 36.0, 'educ': 12.0, 'inlf': 0}, {'age': 47.0, 'educ': 12.0, 'inlf': 0}, {'age': 48.0, 'educ': 16.0, 'inlf': 0}, {'age': 42.0, 'educ': 12.0, 'inlf': 0}, {'age': 33.0, 'educ': 13.0, 'inlf': 0}, {'age': 46.0, 'educ': 10, 'inlf': 0}, {'age': 47.0, 'educ': 12.0, 'inlf': 0}, {'age': 44.0, 'educ': 12.0, 'inlf': 0}, {'age': 36.0, 'educ': 12.0, 'inlf': 0}, {'age': 30, 'educ': 17.0, 'inlf': 0}, {'age': 55.0, 'educ': 14.0, 'inlf': 0}, {'age': 45.0, 'educ': 16.0, 'inlf': 0}, {'age': 47.0, 'educ': 17.0, 'inlf': 0}, {'age': 46.0, 'educ': 12.0, 'inlf': 0}, {'age': 49.0, 'educ': 10, 'inlf': 0}, {'age': 49.0, 'educ': 12.0, 'inlf': 0}, {'age': 45.0, 'educ': 12.0, 'inlf': 0}, {'age': 38.0, 'educ': 17.0, 'inlf': 0}, {'age': 47.0, 'educ': 10.0, 'inlf': 0}, {'age': 54.0, 'educ': 13.0, 'inlf': 0}, {'age': 40, 'educ': 10, 'inlf': 0}, {'age': 43.0, 'educ': 12.0, 'inlf': 0}, {'age': 30, 'educ': 16.0, 'inlf': 0}, {'age': 47.0, 'educ': 17.0, 'inlf': 0}, {'age': 35.0, 'educ': 12.0, 'inlf': 0}, {'age': 45.0, 'educ': 16.0, 'inlf': 0}, {'age': 33.0, 'educ': 12.0, 'inlf': 0}, {'age': 54.0, 'educ': 16.0, 'inlf': 0}, {'age': 35.0, 'educ': 8.0, 'inlf': 0}, {'age': 30, 'educ': 12.0, 'inlf': 0}, {'age': 55.0, 'educ': 12.0, 'inlf': 0}, {'age': 34.0, 'educ': 12.0, 'inlf': 0}, {'age': 38.0, 'educ': 13.0, 'inlf': 0}, {'age': 45.0, 'educ': 10, 'inlf': 0}, {'age': 47.0, 'educ': 12.0, 'inlf': 0}, {'age': 39.0, 'educ': 12.0, 'inlf': 0}, {'age': 36.0, 'educ': 14.0, 'inlf': 0}, {'age': 33.0, 'educ': 12.0, 'inlf': 0}, {'age': 50.0, 'educ': 12.0, 'inlf': 0}, {'age': 58.0, 'educ': 12.0, 'inlf': 0}, {'age': 49.0, 'educ': 17.0, 'inlf': 0}, {'age': 40, 'educ': 14.0, 'inlf': 0}, {'age': 50, 'educ': 12.0, 'inlf': 0}, {'age': 53.0, 'educ': 9.0, 'inlf': 0}, {'age': 36.0, 'educ': 12.0, 'inlf': 0}, {'age': 46.0, 'educ': 12.0, 'inlf': 0}, {'age': 36.0, 'educ': 12.0, 'inlf': 0}, {'age': 53.0, 'educ': 14.0, 'inlf': 0}, {'age': 40.0, 'educ': 16.0, 'inlf': 0}]; var colorScale = d3.scaleLinear() .range(["#e7eef8","#003366"]); xScale.domain(d3.extent(data, function(d) { return d.educ; })); colorScale.domain(d3.extent(data, function(d) {return d.age; })); var simulation = d3.forceSimulation(data) .force("x", d3.forceX(function(d) { return xScale(d.educ); }).strength(1)) .force("y", d3.forceY(function(d) { return d.inlf ? height - 75 : height + 100 })) .force("collide", d3.forceCollide(4)) .stop(); for (var i = 0; i < 120; ++i) simulation.tick(); graphGroup.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(xScale)); var circles = graphGroup.selectAll(null) .data(data) .enter() .append("circle") .attr("r", 3) .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .style('fill', function(d) {return colorScale(d.age)}); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(17)) .attr('y1', height-5) .attr('y2', height-5) .style('stroke',"#000"); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(17)) .attr('y1', height+35) .attr('y2', height+35) .style('stroke',"#000"); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(12)) .attr('y1', height-5) .attr('y2', height-5) .style('stroke',"#b8cce4") .style('stroke-width',"5px"); graphGroup.append('line') .attr('x1', xScale(12)) .attr('x2', xScale(14)) .attr('y1', height-5) .attr('y2', height-5) .style('stroke',"#4f81b9") .style('stroke-width',"5px"); graphGroup.append('rect') .attr('x',xScale(12)) .attr('y', height-10) .attr('width', 5) .attr('height',10) .style('fill', "#f6d18b"); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(11)) .attr('y1', height+35) .attr('y2', height+35) .style('stroke',"#b8cce4") .style('stroke-width',"5px"); graphGroup.append('line') .attr('x1', xScale(11)) .attr('x2', xScale(12)) .attr('y1', height+35) .attr('y2', height+35) .style('stroke',"#4f81b9") .style('stroke-width',"5px"); graphGroup.append('rect') .attr('x',xScale(11.7)) .attr('y', height+30) .attr('width', 5) .attr('height',10) .style('fill', "#f6d18b"); .cells path { fill: none; pointer-events: all; } .cells :hover circle { fill: red; } text { font-size: 17px; font-family: TW Cen MT; } .axis path, .axis line { fill: none; stroke: none; } <script src="https://d3js.org/d3.v5.min.js"></script> As it stands, the stacking shape seems to be a kind of ellipse. I have not found a way to alter the default shape. Question How can I modify the default shape of the circle piling to resemble a pyramid? It doesn't have to be a perfect pyramid, just gunning for more at the bottom less at the top. I can foresee things might be especially tricky for data points where d.educ=12, as that is highly saturated.
If it doesn't have to be perfect, you can achieve the effect by imposing hard constraints on the simulation and adjusting forces. Hard Constraints To form hard constraints (like having a ceiling or a floor), you can manually move points after every tick. For instance, if you want to form a floor for the top points at height - 15, and a ceiling for the bottom points at height + 45, you could change: for (var i = 0; i < 120; ++i) simulation.tick(); to: for (var i = 0; i < 120; ++i) { simulation.tick(); data.forEach(function (d) { if (d.inlf) { d.y = Math.min(d.y, height - 15); } else { d.y = Math.max(d.y, height + 45); } }); } Force Settings After that, you probably want to play around with the strength of forceX and forceY. I also added .iterations to forceCollide, just in case you need to adjust it later. These settings seemed to work okay for me: var simulation = d3.forceSimulation(data) .force("x", d3.forceX(function(d) { return xScale(d.educ); }).strength(0.05)) .force("y", d3.forceY(function(d) { return d.inlf ? height - 15 : height + 45 }).strength(0.1)) .force("collide", d3.forceCollide(4).iterations(1)) .stop(); You may need to increase the number of tick iterations for weak forces or for overlapping circles. I increased it to 240 for this example. for (var i = 0; i < 120; ++i) { simulation.tick(); // ... to: for (var i = 0; i < 240; ++i) { simulation.tick(); // ... Initial positions As pointed out in the comments, the circles are not where they should be! This is due to the weak forces not moving them far enough from their initial position. To fix this, we can set the initial position to be around where we want them. data.forEach(function (d) { d.x = xScale(d.educ); d.y = d.inlf ? height - 15 : height + 45; }); Result After moving the points up, all in all, it looks like: var margins = {top:20, bottom:300, left:30, right:100}; var height = 150; var width = 900; var totalWidth = width+margins.left+margins.right; var totalHeight = height+margins.top+margins.bottom; var svg = d3.select('body') .append('svg') .attr('width', totalWidth) .attr('height', totalHeight); var graphGroup = svg.append('g') .attr('transform', "translate("+margins.left+","+margins.top+")"); var xScale = d3.scaleLinear() .range([0, width]); var data = [{'age': 32.0, 'educ': 12.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 35.0, 'educ': 12.0, 'inlf': 1}, {'age': 34.0, 'educ': 12.0, 'inlf': 1}, {'age': 31, 'educ': 14.0, 'inlf': 1}, {'age': 54.0, 'educ': 12.0, 'inlf': 1}, {'age': 37.0, 'educ': 16.0, 'inlf': 1}, {'age': 54.0, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 12.0, 'inlf': 1}, {'age': 39.0, 'educ': 12.0, 'inlf': 1}, {'age': 33.0, 'educ': 12.0, 'inlf': 1}, {'age': 42.0, 'educ': 11, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 43.0, 'educ': 12.0, 'inlf': 1}, {'age': 43.0, 'educ': 10.0, 'inlf': 1}, {'age': 35.0, 'educ': 11, 'inlf': 1}, {'age': 43.0, 'educ': 12.0, 'inlf': 1}, {'age': 39.0, 'educ': 12.0, 'inlf': 1}, {'age': 45.0, 'educ': 12.0, 'inlf': 1}, {'age': 35.0, 'educ': 12.0, 'inlf': 1}, {'age': 42.0, 'educ': 16.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 13.0, 'inlf': 1}, {'age': 45.0, 'educ': 12.0, 'inlf': 1}, {'age': 31, 'educ': 12.0, 'inlf': 1}, {'age': 43.0, 'educ': 17.0, 'inlf': 1}, {'age': 59.0, 'educ': 12.0, 'inlf': 1}, {'age': 32.0, 'educ': 12.0, 'inlf': 1}, {'age': 31, 'educ': 17.0, 'inlf': 1}, {'age': 42.0, 'educ': 12.0, 'inlf': 1}, {'age': 50.0, 'educ': 11, 'inlf': 1}, {'age': 59.0, 'educ': 16.0, 'inlf': 1}, {'age': 36.0, 'educ': 13.0, 'inlf': 1}, {'age': 51, 'educ': 12.0, 'inlf': 1}, {'age': 45.0, 'educ': 16.0, 'inlf': 1}, {'age': 42.0, 'educ': 11, 'inlf': 1}, {'age': 46.0, 'educ': 12.0, 'inlf': 1}, {'age': 46.0, 'educ': 10.0, 'inlf': 1}, {'age': 51, 'educ': 14.0, 'inlf': 1}, {'age': 30.0, 'educ': 17.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 57.0, 'educ': 12.0, 'inlf': 1}, {'age': 31, 'educ': 16.0, 'inlf': 1}, {'age': 48.0, 'educ': 12.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 34.0, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 16.0, 'inlf': 1}, {'age': 45.0, 'educ': 12.0, 'inlf': 1}, {'age': 51, 'educ': 12.0, 'inlf': 1}, {'age': 30.0, 'educ': 12.0, 'inlf': 1}, {'age': 46.0, 'educ': 12.0, 'inlf': 1}, {'age': 58.0, 'educ': 12.0, 'inlf': 1}, {'age': 37.0, 'educ': 12.0, 'inlf': 1}, {'age': 52.0, 'educ': 8.0, 'inlf': 1}, {'age': 52.0, 'educ': 10.0, 'inlf': 1}, {'age': 31, 'educ': 16.0, 'inlf': 1}, {'age': 55.0, 'educ': 14.0, 'inlf': 1}, {'age': 34.0, 'educ': 17.0, 'inlf': 1}, {'age': 55.0, 'educ': 14.0, 'inlf': 1}, {'age': 39.0, 'educ': 12.0, 'inlf': 1}, {'age': 40.0, 'educ': 14.0, 'inlf': 1}, {'age': 43.0, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 8.0, 'inlf': 1}, {'age': 47.0, 'educ': 12.0, 'inlf': 1}, {'age': 41, 'educ': 12.0, 'inlf': 1}, {'age': 36.0, 'educ': 8.0, 'inlf': 1}, {'age': 46.0, 'educ': 17.0, 'inlf': 1}, {'age': 34.0, 'educ': 12.0, 'inlf': 1}, {'age': 41, 'educ': 12.0, 'inlf': 1}, {'age': 51, 'educ': 12.0, 'inlf': 1}, {'age': 33.0, 'educ': 12.0, 'inlf': 1}, {'age': 52.0, 'educ': 12.0, 'inlf': 1}, {'age': 58.0, 'educ': 9.0, 'inlf': 1}, {'age': 34.0, 'educ': 10.0, 'inlf': 1}, {'age': 31, 'educ': 12.0, 'inlf': 1}, {'age': 48.0, 'educ': 12.0, 'inlf': 1}, {'age': 32.0, 'educ': 12.0, 'inlf': 1}, {'age': 49.0, 'educ': 17.0, 'inlf': 1}, {'age': 32.0, 'educ': 15.0, 'inlf': 1}, {'age': 58.0, 'educ': 12.0, 'inlf': 1}, {'age': 50.0, 'educ': 6.0, 'inlf': 1}, {'age': 60.0, 'educ': 14.0, 'inlf': 1}, {'age': 50.0, 'educ': 12.0, 'inlf': 1}, {'age': 56.0, 'educ': 14.0, 'inlf': 1}, {'age': 51, 'educ': 9.0, 'inlf': 1}, {'age': 54.0, 'educ': 17.0, 'inlf': 1}, {'age': 59.0, 'educ': 13.0, 'inlf': 1}, {'age': 46.0, 'educ': 9.0, 'inlf': 1}, {'age': 46.0, 'educ': 15.0, 'inlf': 1}, {'age': 39.0, 'educ': 12.0, 'inlf': 0}, {'age': 44.0, 'educ': 12.0, 'inlf': 0}, {'age': 33.0, 'educ': 12.0, 'inlf': 0}, {'age': 33.0, 'educ': 12.0, 'inlf': 0}, {'age': 48.0, 'educ': 12.0, 'inlf': 0}, {'age': 30, 'educ': 12.0, 'inlf': 0}, {'age': 45.0, 'educ': 12.0, 'inlf': 0}, {'age': 45.0, 'educ': 12.0, 'inlf': 0}, {'age': 32.0, 'educ': 13.0, 'inlf': 0}, {'age': 47.0, 'educ': 12.0, 'inlf': 0}, {'age': 34.0, 'educ': 13.0, 'inlf': 0}, {'age': 37.0, 'educ': 12.0, 'inlf': 0}, {'age': 36.0, 'educ': 12.0, 'inlf': 0}, {'age': 47.0, 'educ': 12.0, 'inlf': 0}, {'age': 48.0, 'educ': 16.0, 'inlf': 0}, {'age': 42.0, 'educ': 12.0, 'inlf': 0}, {'age': 33.0, 'educ': 13.0, 'inlf': 0}, {'age': 46.0, 'educ': 10, 'inlf': 0}, {'age': 47.0, 'educ': 12.0, 'inlf': 0}, {'age': 44.0, 'educ': 12.0, 'inlf': 0}, {'age': 36.0, 'educ': 12.0, 'inlf': 0}, {'age': 30, 'educ': 17.0, 'inlf': 0}, {'age': 55.0, 'educ': 14.0, 'inlf': 0}, {'age': 45.0, 'educ': 16.0, 'inlf': 0}, {'age': 47.0, 'educ': 17.0, 'inlf': 0}, {'age': 46.0, 'educ': 12.0, 'inlf': 0}, {'age': 49.0, 'educ': 10, 'inlf': 0}, {'age': 49.0, 'educ': 12.0, 'inlf': 0}, {'age': 45.0, 'educ': 12.0, 'inlf': 0}, {'age': 38.0, 'educ': 17.0, 'inlf': 0}, {'age': 47.0, 'educ': 10.0, 'inlf': 0}, {'age': 54.0, 'educ': 13.0, 'inlf': 0}, {'age': 40, 'educ': 10, 'inlf': 0}, {'age': 43.0, 'educ': 12.0, 'inlf': 0}, {'age': 30, 'educ': 16.0, 'inlf': 0}, {'age': 47.0, 'educ': 17.0, 'inlf': 0}, {'age': 35.0, 'educ': 12.0, 'inlf': 0}, {'age': 45.0, 'educ': 16.0, 'inlf': 0}, {'age': 33.0, 'educ': 12.0, 'inlf': 0}, {'age': 54.0, 'educ': 16.0, 'inlf': 0}, {'age': 35.0, 'educ': 8.0, 'inlf': 0}, {'age': 30, 'educ': 12.0, 'inlf': 0}, {'age': 55.0, 'educ': 12.0, 'inlf': 0}, {'age': 34.0, 'educ': 12.0, 'inlf': 0}, {'age': 38.0, 'educ': 13.0, 'inlf': 0}, {'age': 45.0, 'educ': 10, 'inlf': 0}, {'age': 47.0, 'educ': 12.0, 'inlf': 0}, {'age': 39.0, 'educ': 12.0, 'inlf': 0}, {'age': 36.0, 'educ': 14.0, 'inlf': 0}, {'age': 33.0, 'educ': 12.0, 'inlf': 0}, {'age': 50.0, 'educ': 12.0, 'inlf': 0}, {'age': 58.0, 'educ': 12.0, 'inlf': 0}, {'age': 49.0, 'educ': 17.0, 'inlf': 0}, {'age': 40, 'educ': 14.0, 'inlf': 0}, {'age': 50, 'educ': 12.0, 'inlf': 0}, {'age': 53.0, 'educ': 9.0, 'inlf': 0}, {'age': 36.0, 'educ': 12.0, 'inlf': 0}, {'age': 46.0, 'educ': 12.0, 'inlf': 0}, {'age': 36.0, 'educ': 12.0, 'inlf': 0}, {'age': 53.0, 'educ': 14.0, 'inlf': 0}, {'age': 40.0, 'educ': 16.0, 'inlf': 0}]; var colorScale = d3.scaleLinear() .range(["#e7eef8","#003366"]); xScale.domain(d3.extent(data, function(d) { return d.educ; })); colorScale.domain(d3.extent(data, function(d) {return d.age; })); data.forEach(function (d) { d.x = xScale(d.educ); d.y = d.inlf ? height - 15 : height + 45; }); var simulation = d3.forceSimulation(data) .force("x", d3.forceX(function(d) { return xScale(d.educ); }).strength(0.05)) .force("y", d3.forceY(function(d) { return d.inlf ? height - 15 : height + 45 }).strength(0.1)) .force("collide", d3.forceCollide(4).iterations(1)) .stop(); for (var i = 0; i < 240; ++i) { simulation.tick(); data.forEach(function (d) { if (d.inlf) { d.y = Math.min(d.y, height - 15); } else { d.y = Math.max(d.y, height + 45); } }); } graphGroup.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(xScale)); var circles = graphGroup.selectAll(null) .data(data) .enter() .append("circle") .attr("r", 3) .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .style('fill', function(d) {return colorScale(d.age)}); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(17)) .attr('y1', height-5) .attr('y2', height-5) .style('stroke',"#000"); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(17)) .attr('y1', height+35) .attr('y2', height+35) .style('stroke',"#000"); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(12)) .attr('y1', height-5) .attr('y2', height-5) .style('stroke',"#b8cce4") .style('stroke-width',"5px"); graphGroup.append('line') .attr('x1', xScale(12)) .attr('x2', xScale(14)) .attr('y1', height-5) .attr('y2', height-5) .style('stroke',"#4f81b9") .style('stroke-width',"5px"); graphGroup.append('rect') .attr('x',xScale(12)) .attr('y', height-10) .attr('width', 5) .attr('height',10) .style('fill', "#f6d18b"); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(11)) .attr('y1', height+35) .attr('y2', height+35) .style('stroke',"#b8cce4") .style('stroke-width',"5px"); graphGroup.append('line') .attr('x1', xScale(11)) .attr('x2', xScale(12)) .attr('y1', height+35) .attr('y2', height+35) .style('stroke',"#4f81b9") .style('stroke-width',"5px"); graphGroup.append('rect') .attr('x',xScale(11.7)) .attr('y', height+30) .attr('width', 5) .attr('height',10) .style('fill', "#f6d18b"); .cells path { fill: none; pointer-events: all; } .cells :hover circle { fill: red; } text { font-size: 17px; font-family: TW Cen MT; } .axis path, .axis line { fill: none; stroke: none; } <script src="https://d3js.org/d3.v5.min.js"></script>
Little late to the party but if you want a stronger stacked pyramid, I'd abandon the force simulation. Here's a custom implementation with a custom pyramid stacking algorithm: <!DOCTYPE html> <html> <head> <script src="https://d3js.org/d3.v5.min.js"></script> </head> <body> <script> var margins = { top: 20, bottom: 300, left: 30, right: 100 }; var height = 150; var width = 900; var totalWidth = width + margins.left + margins.right; var totalHeight = height + margins.top + margins.bottom; var svg = d3.select('body') .append('svg') .attr('width', totalWidth) .attr('height', totalHeight); var graphGroup = svg.append('g') .attr('transform', "translate(" + margins.left + "," + margins.top + ")"); var xScale = d3.scaleLinear() .range([0, width]); var data = [{ 'age': 32.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 30.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 35.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 34.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 31, 'educ': 14.0, 'inlf': 1 }, { 'age': 54.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 37.0, 'educ': 16.0, 'inlf': 1 }, { 'age': 54.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 48.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 39.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 33.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 42.0, 'educ': 11, 'inlf': 1 }, { 'age': 30.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 43.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 43.0, 'educ': 10.0, 'inlf': 1 }, { 'age': 35.0, 'educ': 11, 'inlf': 1 }, { 'age': 43.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 39.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 45.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 35.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 42.0, 'educ': 16.0, 'inlf': 1 }, { 'age': 30.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 48.0, 'educ': 13.0, 'inlf': 1 }, { 'age': 45.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 31, 'educ': 12.0, 'inlf': 1 }, { 'age': 43.0, 'educ': 17.0, 'inlf': 1 }, { 'age': 59.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 32.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 31, 'educ': 17.0, 'inlf': 1 }, { 'age': 42.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 50.0, 'educ': 11, 'inlf': 1 }, { 'age': 59.0, 'educ': 16.0, 'inlf': 1 }, { 'age': 36.0, 'educ': 13.0, 'inlf': 1 }, { 'age': 51, 'educ': 12.0, 'inlf': 1 }, { 'age': 45.0, 'educ': 16.0, 'inlf': 1 }, { 'age': 42.0, 'educ': 11, 'inlf': 1 }, { 'age': 46.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 46.0, 'educ': 10.0, 'inlf': 1 }, { 'age': 51, 'educ': 14.0, 'inlf': 1 }, { 'age': 30.0, 'educ': 17.0, 'inlf': 1 }, { 'age': 30.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 57.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 31, 'educ': 16.0, 'inlf': 1 }, { 'age': 48.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 30.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 34.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 48.0, 'educ': 16.0, 'inlf': 1 }, { 'age': 45.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 51, 'educ': 12.0, 'inlf': 1 }, { 'age': 30.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 46.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 58.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 37.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 52.0, 'educ': 8.0, 'inlf': 1 }, { 'age': 52.0, 'educ': 10.0, 'inlf': 1 }, { 'age': 31, 'educ': 16.0, 'inlf': 1 }, { 'age': 55.0, 'educ': 14.0, 'inlf': 1 }, { 'age': 34.0, 'educ': 17.0, 'inlf': 1 }, { 'age': 55.0, 'educ': 14.0, 'inlf': 1 }, { 'age': 39.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 40.0, 'educ': 14.0, 'inlf': 1 }, { 'age': 43.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 48.0, 'educ': 8.0, 'inlf': 1 }, { 'age': 47.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 41, 'educ': 12.0, 'inlf': 1 }, { 'age': 36.0, 'educ': 8.0, 'inlf': 1 }, { 'age': 46.0, 'educ': 17.0, 'inlf': 1 }, { 'age': 34.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 41, 'educ': 12.0, 'inlf': 1 }, { 'age': 51, 'educ': 12.0, 'inlf': 1 }, { 'age': 33.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 52.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 58.0, 'educ': 9.0, 'inlf': 1 }, { 'age': 34.0, 'educ': 10.0, 'inlf': 1 }, { 'age': 31, 'educ': 12.0, 'inlf': 1 }, { 'age': 48.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 32.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 49.0, 'educ': 17.0, 'inlf': 1 }, { 'age': 32.0, 'educ': 15.0, 'inlf': 1 }, { 'age': 58.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 50.0, 'educ': 6.0, 'inlf': 1 }, { 'age': 60.0, 'educ': 14.0, 'inlf': 1 }, { 'age': 50.0, 'educ': 12.0, 'inlf': 1 }, { 'age': 56.0, 'educ': 14.0, 'inlf': 1 }, { 'age': 51, 'educ': 9.0, 'inlf': 1 }, { 'age': 54.0, 'educ': 17.0, 'inlf': 1 }, { 'age': 59.0, 'educ': 13.0, 'inlf': 1 }, { 'age': 46.0, 'educ': 9.0, 'inlf': 1 }, { 'age': 46.0, 'educ': 15.0, 'inlf': 1 }, { 'age': 39.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 44.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 33.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 33.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 48.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 30, 'educ': 12.0, 'inlf': 0 }, { 'age': 45.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 45.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 32.0, 'educ': 13.0, 'inlf': 0 }, { 'age': 47.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 34.0, 'educ': 13.0, 'inlf': 0 }, { 'age': 37.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 36.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 47.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 48.0, 'educ': 16.0, 'inlf': 0 }, { 'age': 42.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 33.0, 'educ': 13.0, 'inlf': 0 }, { 'age': 46.0, 'educ': 10, 'inlf': 0 }, { 'age': 47.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 44.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 36.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 30, 'educ': 17.0, 'inlf': 0 }, { 'age': 55.0, 'educ': 14.0, 'inlf': 0 }, { 'age': 45.0, 'educ': 16.0, 'inlf': 0 }, { 'age': 47.0, 'educ': 17.0, 'inlf': 0 }, { 'age': 46.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 49.0, 'educ': 10, 'inlf': 0 }, { 'age': 49.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 45.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 38.0, 'educ': 17.0, 'inlf': 0 }, { 'age': 47.0, 'educ': 10.0, 'inlf': 0 }, { 'age': 54.0, 'educ': 13.0, 'inlf': 0 }, { 'age': 40, 'educ': 10, 'inlf': 0 }, { 'age': 43.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 30, 'educ': 16.0, 'inlf': 0 }, { 'age': 47.0, 'educ': 17.0, 'inlf': 0 }, { 'age': 35.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 45.0, 'educ': 16.0, 'inlf': 0 }, { 'age': 33.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 54.0, 'educ': 16.0, 'inlf': 0 }, { 'age': 35.0, 'educ': 8.0, 'inlf': 0 }, { 'age': 30, 'educ': 12.0, 'inlf': 0 }, { 'age': 55.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 34.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 38.0, 'educ': 13.0, 'inlf': 0 }, { 'age': 45.0, 'educ': 10, 'inlf': 0 }, { 'age': 47.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 39.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 36.0, 'educ': 14.0, 'inlf': 0 }, { 'age': 33.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 50.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 58.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 49.0, 'educ': 17.0, 'inlf': 0 }, { 'age': 40, 'educ': 14.0, 'inlf': 0 }, { 'age': 50, 'educ': 12.0, 'inlf': 0 }, { 'age': 53.0, 'educ': 9.0, 'inlf': 0 }, { 'age': 36.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 46.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 36.0, 'educ': 12.0, 'inlf': 0 }, { 'age': 53.0, 'educ': 14.0, 'inlf': 0 }, { 'age': 40.0, 'educ': 16.0, 'inlf': 0 }]; var colorScale = d3.scaleLinear() .range(["#e7eef8", "#003366"]); xScale.domain(d3.extent(data, function(d) { return d.educ; })); colorScale.domain(d3.extent(data, function(d) { return d.age; })); // bin the data based on top bottom and x-position var bins = d3.nest() .key(function(d) { return [(d.inlf ? 1 : 0), d.educ] }) .entries(data); // calculate positions (build the pyramid) var r = 3; bins.forEach( (bin) => { var ds = bin.values; var br = 1, n = 1; while (br < ds.length){ br += n; n += 1; } n -= 1 var c = 0; rn = n; g = 0; var s = r * 2 + 3; ds.forEach((d)=>{ d.y = (rn - n) * s; d.x = (c * s); d.g = g; c += 1; if (c == n){ c = 0; n -= 1; g += 1 } }); }); // re-bin by age and row so that we can traslate the rows var binsAndRow = d3.nest() .key(function(d) { return [(d.inlf ? 1 : 0), d.educ]; }) .key(function(d) { return d.g; }) .entries(data); // draw circles var x = graphGroup.selectAll("g") .data(binsAndRow) .enter() .append("g") .attr("class", (d) => "bin " + "b" + d.key) .selectAll("g") .data( (d) => { return d.values }) .enter() .append("g") .attr("class", (d) => "row " + "r" + d.key) .selectAll("circle") .data( (d) => { return d.values }) .enter() .append("circle") .attr("r", 3) .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .style('fill', function(d) {return colorScale(d.age)}); // position the pyramids graphGroup .selectAll(".bin") .attr("transform", function(d){ var bb = this.getBBox(), xPos = (xScale(d.key.split(",")[1]) - bb.width/2), isFlip = d.key.startsWith("1"), yPos = isFlip ? height - 20 : height + 50, t = "translate(" + xPos + "," + yPos + ")"; if (isFlip) t+= "scale(1,-1)"; return t; }) .selectAll(".row") .attr("transform", function(d) { var pbb = this.parentNode.getBBox(); var bb = this.getBBox(); return "translate(" + ((pbb.width - bb.width)/2) + "," + 0 + ")"; }); graphGroup.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(xScale)); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(17)) .attr('y1', height - 5) .attr('y2', height - 5) .style('stroke', "#000"); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(17)) .attr('y1', height + 35) .attr('y2', height + 35) .style('stroke', "#000"); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(12)) .attr('y1', height - 5) .attr('y2', height - 5) .style('stroke', "#b8cce4") .style('stroke-width', "5px"); graphGroup.append('line') .attr('x1', xScale(12)) .attr('x2', xScale(14)) .attr('y1', height - 5) .attr('y2', height - 5) .style('stroke', "#4f81b9") .style('stroke-width', "5px"); graphGroup.append('rect') .attr('x', xScale(12)) .attr('y', height - 10) .attr('width', 5) .attr('height', 10) .style('fill', "#f6d18b"); graphGroup.append('line') .attr('x1', xScale(5)) .attr('x2', xScale(11)) .attr('y1', height + 35) .attr('y2', height + 35) .style('stroke', "#b8cce4") .style('stroke-width', "5px"); graphGroup.append('line') .attr('x1', xScale(11)) .attr('x2', xScale(12)) .attr('y1', height + 35) .attr('y2', height + 35) .style('stroke', "#4f81b9") .style('stroke-width', "5px"); graphGroup.append('rect') .attr('x', xScale(11.7)) .attr('y', height + 30) .attr('width', 5) .attr('height', 10) .style('fill', "#f6d18b"); </script> </body> </html>
Solution of Jsprit is not correct if shipments have multiple size of dimension
I am new to Jsprit. I tried to use multiple size of dimension in my shipment list. For example, some shipments I added size of dimension with WHEELCHAIRSPACE_INDEX and some shipments I use PASSENGERSEATS_INDEX in my createJob(). However the output seem like wrong. public class Test { static int WHEELCHAIRSPACE_INDEX = 0; static int PASSENGERSEATS_INDEX = 1; private VehicleType vehicleType_wheelchair; private VehicleType vehicleType_solelypassenger; private VehicleImpl vehicle1,vehicle1_2,vehicle2,vehicle2_2; private VehicleRoutingProblem.Builder vrpBuilder; private HardRouteConstraint wheelchair_bus_passenger_pickup_constraint; private VehicleRoutingAlgorithm algorithm; private VehicleRoutingProblem problem; private VehicleRoutingTransportCosts costMatrix; private static final String FILENAME = "C:\\Users\\admin\\Desktop\\test.txt"; public static void main(String[] args) { // TODO Auto-generated method stub Examples.createOutputFolder(); Test test = new Test(); test.createVehicleType(); test.createVehicle(); test.createJob(); test.createConstraint(); test.createProblem(); test.searchSolution(); } private void searchSolution() { Collection<VehicleRoutingProblemSolution> solutions = algorithm.searchSolutions(); VehicleRoutingProblemSolution bestSolution = Solutions.bestOf(solutions); SolutionPrinter.print(problem, bestSolution, SolutionPrinter.Print.VERBOSE); Plotter problemPlotter = new Plotter(problem); problemPlotter.plotShipments(true); problemPlotter.setLabel(Plotter.Label.SIZE); problemPlotter.plot("output/transportOfDisabledPeopleExample_problem.png", "disabled people tp"); Plotter solutionPlotter = new Plotter(problem, Solutions.bestOf(solutions)); solutionPlotter.plotShipments(true); solutionPlotter.setLabel(Plotter.Label.SIZE); solutionPlotter.plot("output/transportOfDisabledPeopleExample_solution.png", "disabled people tp"); } private void createProblem() { StateManager stateManager = new StateManager(problem); ConstraintManager constraintManager = new ConstraintManager(problem, stateManager); constraintManager.addConstraint(wheelchair_bus_passenger_pickup_constraint); algorithm = Jsprit.Builder.newInstance(problem).setStateAndConstraintManager(stateManager,constraintManager).buildAlgorithm(); algorithm.setPrematureAlgorithmTermination(new IterationWithoutImprovementTermination(100)); } private void createConstraint() { wheelchair_bus_passenger_pickup_constraint = new HardRouteConstraint() { #Override public boolean fulfilled(JobInsertionContext insertionContext) { return true; } }; } private void createJob() { com.graphhopper.jsprit.core.problem.job.Shipment.Builder builder1 = Shipment.Builder.newInstance("1"); builder1.addSizeDimension(WHEELCHAIRSPACE_INDEX, 1); builder1.setPickupLocation(Location.newInstance("1a")); builder1.setDeliveryLocation(Location.newInstance("1b")); builder1.setDeliveryServiceTime(0); Shipment shipment1 = builder1.build(); com.graphhopper.jsprit.core.problem.job.Shipment.Builder builder2 = Shipment.Builder.newInstance("2"); builder2.addSizeDimension(PASSENGERSEATS_INDEX, 1); builder2.setPickupLocation(Location.newInstance("2a")); builder2.setDeliveryLocation(Location.newInstance("2b")); builder2.setDeliveryServiceTime(0); Shipment shipment2 = builder2.build(); com.graphhopper.jsprit.core.problem.job.Shipment.Builder builder3 = Shipment.Builder.newInstance("3"); builder3.addSizeDimension(WHEELCHAIRSPACE_INDEX, 1); builder3.setPickupLocation(Location.newInstance("3a")); builder3.setDeliveryLocation(Location.newInstance("3b")); builder3.setDeliveryServiceTime(0); Shipment shipment3 = builder3.build(); com.graphhopper.jsprit.core.problem.job.Shipment.Builder builder4 = Shipment.Builder.newInstance("4"); builder4.addSizeDimension(PASSENGERSEATS_INDEX, 1); builder4.setPickupLocation(Location.newInstance("4a")); builder4.setDeliveryLocation(Location.newInstance("4b")); builder4.setDeliveryServiceTime(0); Shipment shipment4 = builder4.build(); com.graphhopper.jsprit.core.problem.job.Shipment.Builder builder5 = Shipment.Builder.newInstance("5"); builder5.addSizeDimension(WHEELCHAIRSPACE_INDEX, 1); builder5.setPickupLocation(Location.newInstance("5a")); builder5.setDeliveryLocation(Location.newInstance("5b")); builder5.setDeliveryServiceTime(0); Shipment shipment5 = builder5.build(); setTransportCost(); vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); vrpBuilder.addVehicle(vehicle1); vrpBuilder.addVehicle(vehicle2); vrpBuilder.addVehicle(vehicle1_2); vrpBuilder.addVehicle(vehicle2_2); vrpBuilder.addJob(shipment1).addJob(shipment2).addJob(shipment3).addJob(shipment4); vrpBuilder.addJob(shipment5); vrpBuilder.setFleetSize(FleetSize.FINITE); vrpBuilder.setRoutingCost(costMatrix); problem = vrpBuilder.build(); } private void setTransportCost() { // TODO Auto-generated method stub VehicleRoutingTransportCostsMatrix.Builder costMatrixBuilder = VehicleRoutingTransportCostsMatrix.Builder.newInstance(true); costMatrixBuilder.addTransportTime("0", "0", 0.0); costMatrixBuilder.addTransportTime("0", "1a", 2.0); costMatrixBuilder.addTransportTime("0", "1b", 3.3); costMatrixBuilder.addTransportTime("0", "2a", 2.0); costMatrixBuilder.addTransportTime("0", "2b", 4.3); costMatrixBuilder.addTransportTime("0", "3a", 3.5); costMatrixBuilder.addTransportTime("0", "3b", 3.0); costMatrixBuilder.addTransportTime("0", "4a", 1.5); costMatrixBuilder.addTransportTime("0", "4b", 2.7); costMatrixBuilder.addTransportTime("0", "5a", 1.2); costMatrixBuilder.addTransportTime("0", "5b", 3.5); costMatrixBuilder.addTransportTime("1a", "0", 2.0); costMatrixBuilder.addTransportTime("1a", "1a", 0.0); costMatrixBuilder.addTransportTime("1a", "1b", 2.5); costMatrixBuilder.addTransportTime("1a", "2a", 2.3); costMatrixBuilder.addTransportTime("1a", "2b", 4.4); costMatrixBuilder.addTransportTime("1a", "3a", 4.0); costMatrixBuilder.addTransportTime("1a", "3b", 4.0); costMatrixBuilder.addTransportTime("1a", "4a", 2.3); costMatrixBuilder.addTransportTime("1a", "4b", 4.5); costMatrixBuilder.addTransportTime("1a", "5a", 3.2); costMatrixBuilder.addTransportTime("1a", "5b", 1.5); costMatrixBuilder.addTransportTime("1b", "0", 3.3); costMatrixBuilder.addTransportTime("1b", "1a", 2.5); costMatrixBuilder.addTransportTime("1b", "1b", 0.0); costMatrixBuilder.addTransportTime("1b", "2a", 1.7); costMatrixBuilder.addTransportTime("1b", "2b", 2.2); costMatrixBuilder.addTransportTime("1b", "3a", 2.5); costMatrixBuilder.addTransportTime("1b", "3b", 3.4); costMatrixBuilder.addTransportTime("1b", "4a", 4.5); costMatrixBuilder.addTransportTime("1b", "4b", 5.0); costMatrixBuilder.addTransportTime("1b", "5a", 4.5); costMatrixBuilder.addTransportTime("1b", "5b", 2.2); costMatrixBuilder.addTransportTime("2a", "0", 2.0); costMatrixBuilder.addTransportTime("2a", "1a", 2.3); costMatrixBuilder.addTransportTime("2a", "1b", 1.7); costMatrixBuilder.addTransportTime("2a", "2a", 0.0); costMatrixBuilder.addTransportTime("2a", "2b", 2.3); costMatrixBuilder.addTransportTime("2a", "3a", 1.7); costMatrixBuilder.addTransportTime("2a", "3b", 1.8); costMatrixBuilder.addTransportTime("2a", "4a", 3.3); costMatrixBuilder.addTransportTime("2a", "4b", 3.2); costMatrixBuilder.addTransportTime("2a", "5a", 3.0); costMatrixBuilder.addTransportTime("2a", "5b", 2.9); costMatrixBuilder.addTransportTime("2b", "0", 4.3); costMatrixBuilder.addTransportTime("2b", "1a", 4.4); costMatrixBuilder.addTransportTime("2b", "1b", 2.2); costMatrixBuilder.addTransportTime("2b", "2a", 2.3); costMatrixBuilder.addTransportTime("2b", "2b", 0.0); costMatrixBuilder.addTransportTime("2b", "3a", 1.2); costMatrixBuilder.addTransportTime("2b", "3b", 2.8); costMatrixBuilder.addTransportTime("2b", "4a", 5.7); costMatrixBuilder.addTransportTime("2b", "4b", 4.5); costMatrixBuilder.addTransportTime("2b", "5a", 5.2); costMatrixBuilder.addTransportTime("2b", "5b", 4.3); costMatrixBuilder.addTransportTime("3a", "0", 3.5); costMatrixBuilder.addTransportTime("3a", "1a", 4.0); costMatrixBuilder.addTransportTime("3a", "1b", 2.5); costMatrixBuilder.addTransportTime("3a", "2a", 1.7); costMatrixBuilder.addTransportTime("3a", "2b", 1.2); costMatrixBuilder.addTransportTime("3a", "3a", 0.0); costMatrixBuilder.addTransportTime("3a", "3b", 1.5); costMatrixBuilder.addTransportTime("3a", "4a", 5.0); costMatrixBuilder.addTransportTime("3a", "4b", 3.3); costMatrixBuilder.addTransportTime("3a", "5a", 4.2); costMatrixBuilder.addTransportTime("3a", "5b", 4.3); costMatrixBuilder.addTransportTime("3b", "0", 3.0); costMatrixBuilder.addTransportTime("3b", "1a", 4.0); costMatrixBuilder.addTransportTime("3b", "1b", 3.4); costMatrixBuilder.addTransportTime("3b", "2a", 1.8); costMatrixBuilder.addTransportTime("3b", "2b", 2.8); costMatrixBuilder.addTransportTime("3b", "3a", 1.5); costMatrixBuilder.addTransportTime("3b", "3b", 0.0); costMatrixBuilder.addTransportTime("3b", "4a", 4.3); costMatrixBuilder.addTransportTime("3b", "4b", 1.8); costMatrixBuilder.addTransportTime("3b", "5a", 2.8); costMatrixBuilder.addTransportTime("3b", "5b", 5.0); costMatrixBuilder.addTransportTime("4a", "0", 1.5); costMatrixBuilder.addTransportTime("4a", "1a", 2.3); costMatrixBuilder.addTransportTime("4a", "1b", 4.5); costMatrixBuilder.addTransportTime("4a", "2a", 3.3); costMatrixBuilder.addTransportTime("4a", "2b", 5.7); costMatrixBuilder.addTransportTime("4a", "3a", 5.0); costMatrixBuilder.addTransportTime("4a", "3b", 4.3); costMatrixBuilder.addTransportTime("4a", "4a", 0.0); costMatrixBuilder.addTransportTime("4a", "4b", 3.6); costMatrixBuilder.addTransportTime("4a", "5a", 1.7); costMatrixBuilder.addTransportTime("4a", "5b", 3.8); costMatrixBuilder.addTransportTime("4b", "0", 2.7); costMatrixBuilder.addTransportTime("4b", "1a", 4.5); costMatrixBuilder.addTransportTime("4b", "1b", 5.0); costMatrixBuilder.addTransportTime("4b", "2a", 3.2); costMatrixBuilder.addTransportTime("4b", "2b", 4.5); costMatrixBuilder.addTransportTime("4b", "3a", 3.3); costMatrixBuilder.addTransportTime("4b", "3b", 1.8); costMatrixBuilder.addTransportTime("4b", "4a", 3.6); costMatrixBuilder.addTransportTime("4b", "4b", 0.0); costMatrixBuilder.addTransportTime("4b", "5a", 1.9); costMatrixBuilder.addTransportTime("4b", "5b", 5.7); costMatrixBuilder.addTransportTime("5a", "0", 1.2); costMatrixBuilder.addTransportTime("5a", "1a", 3.2); costMatrixBuilder.addTransportTime("5a", "1b", 4.5); costMatrixBuilder.addTransportTime("5a", "2a", 3.0); costMatrixBuilder.addTransportTime("5a", "2b", 5.2); costMatrixBuilder.addTransportTime("5a", "3a", 4.2); costMatrixBuilder.addTransportTime("5a", "3b", 2.8); costMatrixBuilder.addTransportTime("5a", "4a", 1.7); costMatrixBuilder.addTransportTime("5a", "4b", 1.9); costMatrixBuilder.addTransportTime("5a", "5a", 0.0); costMatrixBuilder.addTransportTime("5a", "5b", 4.7); costMatrixBuilder.addTransportTime("5b", "0", 3.5); costMatrixBuilder.addTransportTime("5b", "1a", 1.5); costMatrixBuilder.addTransportTime("5b", "1b", 2.2); costMatrixBuilder.addTransportTime("5b", "2a", 2.9); costMatrixBuilder.addTransportTime("5b", "2b", 4.3); costMatrixBuilder.addTransportTime("5b", "3a", 4.3); costMatrixBuilder.addTransportTime("5b", "3b", 5.0); costMatrixBuilder.addTransportTime("5b", "4a", 3.8); costMatrixBuilder.addTransportTime("5b", "4b", 5.7); costMatrixBuilder.addTransportTime("5b", "5a", 4.7); costMatrixBuilder.addTransportTime("5b", "5b", 0.0); costMatrix = costMatrixBuilder.build(); } private void createVehicle() { Builder vehicleBuilder1 = VehicleImpl.Builder.newInstance("wheelchair_bus"); vehicleBuilder1.setStartLocation(Location.newInstance("0")).setEndLocation(Location.newInstance("0")); vehicleBuilder1.setType(vehicleType_wheelchair); vehicleBuilder1.setEarliestStart(0); vehicleBuilder1.setLatestArrival(10); vehicle1 = vehicleBuilder1.build(); Builder vehicleBuilder1_2 = VehicleImpl.Builder.newInstance("wheelchair_bus_2"); vehicleBuilder1_2.setStartLocation(Location.newInstance("0")).setEndLocation(Location.newInstance("0")); vehicleBuilder1_2.setType(vehicleType_wheelchair); vehicleBuilder1_2.setEarliestStart(0); vehicleBuilder1_2.setLatestArrival(10); vehicle1_2 = vehicleBuilder1_2.build(); Builder vehicleBuilder2 = VehicleImpl.Builder.newInstance("passenger_bus"); vehicleBuilder2.setStartLocation(Location.newInstance("0")).setEndLocation(Location.newInstance("0")); vehicleBuilder2.setType(vehicleType_solelypassenger); vehicleBuilder2.setEarliestStart(0); vehicleBuilder2.setLatestArrival(10); vehicle2 = vehicleBuilder2.build(); Builder vehicleBuilder2_2 = VehicleImpl.Builder.newInstance("passenger_bus_2"); vehicleBuilder2_2.setStartLocation(Location.newInstance("0")).setEndLocation(Location.newInstance("0")); vehicleBuilder2_2.setType(vehicleType_solelypassenger); vehicleBuilder2_2.setEarliestStart(0); vehicleBuilder2_2.setLatestArrival(10); vehicle2_2 = vehicleBuilder2_2.build(); } private void createVehicleType() { VehicleTypeImpl.Builder wheelChairTypeBuilder = VehicleTypeImpl.Builder.newInstance("wheelChairBusType"); wheelChairTypeBuilder.addCapacityDimension(WHEELCHAIRSPACE_INDEX, 2); wheelChairTypeBuilder.addCapacityDimension(PASSENGERSEATS_INDEX, 4); //wheelChairTypeBuilder.setCostPerDistance(1); wheelChairTypeBuilder.setCostPerTransportTime(2); vehicleType_wheelchair = wheelChairTypeBuilder.build(); VehicleTypeImpl.Builder soleyPassengerTypeBuilder = VehicleTypeImpl.Builder.newInstance("passengerBusType"); soleyPassengerTypeBuilder.addCapacityDimension(PASSENGERSEATS_INDEX, 6); //soleyPassengerTypeBuilder.setCostPerDistance(1); soleyPassengerTypeBuilder.setCostPerTransportTime(2); vehicleType_solelypassenger = soleyPassengerTypeBuilder.build(); } private static Location loc(Coordinate coordinate) { return Location.Builder.newInstance().setCoordinate(coordinate).build(); } } This is the output Shipment 1 and shipment 5 is not assigned to job list. However, if I change add all size of dimension to PASSENGERSEATS_INDEX then all jobs are assigned correctly. Does that mean that the algorithm is not support multiple size of dimension? I found that there is a solution which assign 4 jobs when I stepped into the code in debugging mode. However,it is consider as not accepted solution in Jsprit. From logically thinking, this solution should be the correct solution because it minimize the unassigned job (compare to original solution only assign 3 job).
When you built your cost matrix, you only specified travel time, not distances. By default, the cost of an unassigned job depends on the the (maximum) distances between the pickup and delivery locations. Since distances aren't set, there is no penalty for unassigned jobs. An easy solution would be to add some reasonable distance metric when you build the cost matrix: private void setTransportCost() { VehicleRoutingTransportCostsMatrix.Builder costMatrixBuilder = VehicleRoutingTransportCostsMatrix.Builder.newInstance(true); String[] locationIds = new String[] {"0", "1a", "1b", "2a", "2b", "3a", "3b", "4a", "4b", "5a", "5b"}; for (String l1: locationIds) { for (String l2: locationIds) { if (!l1.equals(l2)) { costMatrixBuilder.addTransportDistance(l1, l2, 5); } } } // ... on with original implementation ...
How do you render a 3D object (where the vertices and normals are stored in a header file) using OpenGL ES for iPhone development?
I created a simple 3D cube object using blender and used Jeff Lamarche's blender export to store it in a header file and added it into my project. However I am having trouble rendering it. header file: //If not using MC3D, change 1 to 0 to add needed types #if 0 #import "MC3DTypes.h" #else struct texCoord { GLfloat u; GLfloat v; }; typedef struct texCoord texCoord; typedef texCoord* texCoordPtr; typedef struct vec2 vec2; typedef vec2* vec2Ptr; struct vec3 { GLfloat x; GLfloat y; GLfloat z; }; typedef struct vec3 vec3; typedef vec3* vec3Ptr; struct vec4 { GLfloat x; GLfloat y; GLfloat z; GLfloat w; }; typedef struct vec4 vec4; typedef vec4* vec4Ptr; #endif struct vertexData { vec3 vertex; vec3 normal; }; typedef struct vertexData vertexData; typedef vertexData* vertexDataPtr; static const vertexData MeshVertexData[] = { {/*v:*/{1.000000, -1.000000, -1.000000}, /*n:*/{0.577349, 0.577349, -0.577349} }, {/*v:*/{1.000000, -1.000000, 1.000000}, /*n:*/{0.577349, -0.577349, -0.577349} }, {/*v:*/{-1.000000, -1.000000, 1.000000}, /*n:*/{-0.577349, -0.577349, -0.577349} }, {/*v:*/{1.000000, 1.000000, -1.000000}, /*n:*/{0.577349, 0.577349, 0.577349} }, {/*v:*/{-1.000000, 1.000000, -1.000000}, /*n:*/{-0.577349, 0.577349, 0.577349} }, {/*v:*/{0.999999, 1.000000, 1.000001}, /*n:*/{0.577349, -0.577349, 0.577349} }, {/*v:*/{1.000000, -1.000000, -1.000000}, /*n:*/{0.577349, 0.577349, -0.577349} }, {/*v:*/{1.000000, 1.000000, -1.000000}, /*n:*/{0.577349, 0.577349, 0.577349} }, {/*v:*/{1.000000, -1.000000, 1.000000}, /*n:*/{0.577349, -0.577349, -0.577349} }, {/*v:*/{1.000000, -1.000000, 1.000000}, /*n:*/{0.577349, -0.577349, -0.577349} }, {/*v:*/{0.999999, 1.000000, 1.000001}, /*n:*/{0.577349, -0.577349, 0.577349} }, {/*v:*/{-1.000000, -1.000000, 1.000000}, /*n:*/{-0.577349, -0.577349, -0.577349} }, {/*v:*/{-1.000000, -1.000000, 1.000000}, /*n:*/{-0.577349, -0.577349, -0.577349} }, {/*v:*/{-1.000000, 1.000000, 1.000000}, /*n:*/{-0.577349, -0.577349, 0.577349} }, {/*v:*/{-1.000000, 1.000000, -1.000000}, /*n:*/{-0.577349, 0.577349, 0.577349} }, {/*v:*/{1.000000, 1.000000, -1.000000}, /*n:*/{0.577349, 0.577349, 0.577349} }, {/*v:*/{1.000000, -1.000000, -1.000000}, /*n:*/{0.577349, 0.577349, -0.577349} }, {/*v:*/{-1.000000, 1.000000, -1.000000}, /*n:*/{-0.577349, 0.577349, 0.577349} }, {/*v:*/{-1.000000, -1.000000, -1.000000}, /*n:*/{-0.577349, 0.577349, -0.577349} }, {/*v:*/{1.000000, -1.000000, -1.000000}, /*n:*/{0.577349, 0.577349, -0.577349} }, {/*v:*/{-1.000000, -1.000000, 1.000000}, /*n:*/{-0.577349, -0.577349, -0.577349} }, {/*v:*/{1.000000, 1.000000, -1.000000}, /*n:*/{0.577349, 0.577349, 0.577349} }, {/*v:*/{0.999999, 1.000000, 1.000001}, /*n:*/{0.577349, -0.577349, 0.577349} }, {/*v:*/{1.000000, -1.000000, 1.000000}, /*n:*/{0.577349, -0.577349, -0.577349} }, {/*v:*/{1.000000, -1.000000, -1.000000}, /*n:*/{0.577349, 0.577349, -0.577349} }, {/*v:*/{-1.000000, -1.000000, -1.000000}, /*n:*/{-0.577349, 0.577349, -0.577349} }, {/*v:*/{-1.000000, 1.000000, -1.000000}, /*n:*/{-0.577349, 0.577349, 0.577349} }, {/*v:*/{-1.000000, 1.000000, -1.000000}, /*n:*/{-0.577349, 0.577349, 0.577349} }, {/*v:*/{-1.000000, 1.000000, 1.000000}, /*n:*/{-0.577349, -0.577349, 0.577349} }, {/*v:*/{0.999999, 1.000000, 1.000001}, /*n:*/{0.577349, -0.577349, 0.577349} }, {/*v:*/{-1.000000, -1.000000, -1.000000}, /*n:*/{-0.577349, 0.577349, -0.577349} }, {/*v:*/{-1.000000, -1.000000, 1.000000}, /*n:*/{-0.577349, -0.577349, -0.577349} }, {/*v:*/{-1.000000, 1.000000, -1.000000}, /*n:*/{-0.577349, 0.577349, 0.577349} }, {/*v:*/{0.999999, 1.000000, 1.000001}, /*n:*/{0.577349, -0.577349, 0.577349} }, {/*v:*/{-1.000000, 1.000000, 1.000000}, /*n:*/{-0.577349, -0.577349, 0.577349} }, {/*v:*/{-1.000000, -1.000000, 1.000000}, /*n:*/{-0.577349, -0.577349, -0.577349} }, }; // Example OpenGL ES 1.1 Drawing Code: // glEnableClientState(GL_VERTEX_ARRAY); // glEnableClientState(GL_NORMAL_ARRAY); // glVertexPointer(3, GL_FLOAT, sizeof(VertexData3D), &MeshVertexData[0].vertex); // glNormalPointer(GL_FLOAT, sizeof(VertexData3D), &MeshVertexData[0].normal); // glDrawArrays(GL_TRIANGLES, 0, kMeshNumberOfVertices); // glDisableClientState(GL_VERTEX_ARRAY); // glDisableClientState(GL_NORMAL_ARRAY); And here is the code I am using: // // ES1Renderer.m // glTestingGround // // Created by David Jacobs on 3/8/10. // Copyright Stanford University 2010. All rights reserved. // #import "ES1Renderer.h" #import "banana.h" #import "PVRTexture.h" #import "cubefr.h" #define BUFFER_OFFSET(x)((char *)NULL+(x)) #implementation ES1Renderer // Create an ES 1.1 context - (id) init { if (self = [super init]) { context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; if (!context || ![EAGLContext setCurrentContext:context]) { [self release]; return nil; } // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer glGenFramebuffersOES(1, &defaultFramebuffer); glGenRenderbuffersOES(1, &colorRenderbuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer); glEnable(GL_CULL_FACE); glEnable(GL_CCW); // ###################### Texture Demo ################################# /*NSString * path = [[NSBundle mainBundle] pathForResource: #"banana" ofType:#"pvrtc"]; texture = [[PVRTexture alloc] initWithContentsOfFile:path]; glBindTexture(GL_TEXTURE_2D, texture.name); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);*/ // ##################################################################### } return self; } - (void) render { // Replace the implementation of this method to do your own custom drawing // This application only creates a single context which is already set current at this point. // This call is redundant, but needed if dealing with multiple contexts. [EAGLContext setCurrentContext:context]; // This application only creates a single default framebuffer which is already bound at this point. // This call is redundant, but needed if dealing with multiple framebuffers. glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer); //obj2opengl.pl banana.obj //To include the converted object all you have to do is // include generated arrays // --------------------------------------------------------------- // Determines the Normalized Device Coordinate -> Window Coordinate Transform // --------------------------------------------------------------- glViewport(0, 0, backingWidth, backingHeight); // --------------------------------------------------------------- // Determines the Eye Coordinate -> Clip Coordinate Transform // --------------------------------------------------------------- glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustumf(-2, 2, -3, 3, 5, 20); // --------------------------------------------------------------- // Determines the Object Coordinate -> Eye Coordinate Transform // --------------------------------------------------------------- glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0, 0, -8); glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // ####################### Geometry Demo ######################### /* #define TETRA_FRONT 0.0, 0.0, 1.0 #define TETRA_RIGHT 0.943, 0.0, -0.333 #define TETRA_TOP -0.471, 0.816, -0.333 #define TETRA_LEFT -0.471, -0.816, -0.333 #define v1 1.000000, -1.000000, -1.000000 #define v2 1.000000, -1.000000, 1.000000 #define v3 -1.000000, -1.000000, 1.000000 #define v4 -1.000000, -1.000000, -1.000000 #define v5 1.000000, 1.000000, -1.000000 #define v6 0.999999, 1.000000, 1.000001 #define v7 -1.000000, 1.000000, 1.000000 #define v8 -1.000000, 1.000000, -1.000000 static const GLfloat tetraVertices[] = { TETRA_TOP, TETRA_RIGHT, TETRA_LEFT, TETRA_FRONT, TETRA_TOP, TETRA_RIGHT, }; static const GLfloat newVertices[] = { v1, v2, v3, v4, v5, v6, v7, v8 }; #define RED 255, 0, 0,255 #define GREEN 0,255, 0,255 #define BLUE 0, 0,255,255 #define YELLOW 255,255, 0,255 static const GLubyte tetraColors[] = { RED, GREEN, BLUE, YELLOW, RED, GREEN, }; static const GLubyte newColors[] = { RED, RED, RED, RED, RED, RED, RED, RED }; static float t = 0.0; t += 1/30.f; //glVertexPointer(3, GL_FLOAT, 0, tetraVertices); glVertexPointer(3, GL_FLOAT, 0, newVertices); glEnableClientState(GL_VERTEX_ARRAY); glColorPointer(4, GL_UNSIGNED_BYTE, 0, newColors); //glColorPointer(4, GL_UNSIGNED_BYTE,0, tetraColors); glEnableClientState(GL_COLOR_ARRAY); glPushMatrix(); glTranslatef(0, 0, -3); glRotatef(30*t, 0, 1, 0); glScalef(3, 3, 3); glDrawArrays(GL_TRIANGLE_STRIP, 0, 6); glPopMatrix(); glPushMatrix(); glTranslatef(2, 2, 0); glRotatef(-30*t, 1, 0, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 6); glPopMatrix(); glPushMatrix(); glTranslatef(2, -2, 0); glRotatef(-60*t, 1, 0, 1); glScalef(2, 2, 2); glDrawArrays(GL_TRIANGLE_STRIP, 0, 6); glPopMatrix(); */ //glDisableClientState(GL_VERTEX_ARRAY); //glDisableClientState(GL_NORMAL_ARRAY); // set input data to arrays //glVertexPointer(3, GL_FLOAT, 0, MeshVertexData); //glEnableClientState(GL_VERTEX_ARRAY); //glNormalPointer(GL_FLOAT, 0,indexes); //glEnableClientState(GL_NORMAL_ARRAY); //glTexCoordPointer(2, GL_FLOAT, 0, bananaTexCoords); //glEnableClientState(GL_TEXTURE_COORD_ARRAY); //glEnable(GL_TEXTURE_2D); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(3, GL_FLOAT, sizeof(MeshVertexData), &MeshVertexData[0].vertex); glNormalPointer(GL_FLOAT, sizeof(MeshVertexData), &MeshVertexData[0].normal); //glTexCoordPointer(2, GL_FLOAT, sizeof(TexturedVertexData3D), &VertexData[0].texCoord); static float t = 0.0; t += 1/30.f; glPushMatrix(); glTranslatef(0, 0, 0); glRotatef(30*t, 0, 1, 0); glScalef(1, 1, 1); //glDrawArrays(GL_TRIANGLES, 0, vertex_count); glDrawArrays(GL_TRIANGLES, 0, sizeof(&MeshVertexData[0].vertex)); glPopMatrix(); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glLoadIdentity(); //glDisableClientState(GL_TEXTURE_COORD_ARRAY); //glDisable(GL_TEXTURE_2D); // ############################################################### // Enable lighting // This application only creates a single color renderbuffer which is already bound at this point. // This call is redundant, but needed if dealing with multiple renderbuffers. glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; } - (BOOL) resizeFromLayer:(CAEAGLLayer *)layer { // Allocate color buffer backing based on the current layer size glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer); [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer]; glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { NSLog(#"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); return NO; } return YES; } - (void) dealloc { // Tear down GL if (defaultFramebuffer) { glDeleteFramebuffersOES(1, &defaultFramebuffer); defaultFramebuffer = 0; } if (colorRenderbuffer) { glDeleteRenderbuffersOES(1, &colorRenderbuffer); colorRenderbuffer = 0; } // Tear down context if ([EAGLContext currentContext] == context) [EAGLContext setCurrentContext:nil]; [context release]; context = nil; [super dealloc]; } #end Any help is greatly appreciated. Also any tutorials on OpenGL ES would be great. Thanks in advance
It's not enough to have the vertex and normal data from Blender, you need the projection and model-view matrices and camera and light positions too. A better approach is to export to a file format that includes that, such as Collada, or POD (if you want to keep it simple). The PowerVR SDK includes tools to export from Blender and even a basic game engine that will parse POD. You also get texture coordinates and animations this way. Check out my first article here: http://montgomery1.com/opengl/