Public
Edited
Apr 10
Insert cell
Insert cell
Insert cell
Insert cell
html`<svg width="100" height="100">
<circle cx="40" cy="60" r="20" style="fill:red;">
</circle>`
Insert cell
html`<svg width="110" height="110">
<polygon points="50,5 20,99 95,39 5,39 80,99" style="fill:blue;stroke:red;stroke-width:5;fill-rule:evenodd;" />
</circle>`
Insert cell
Insert cell
// tres círculos con el mismo centro, distinto tamaño. Se dibujan de mayor a menor según el radio para que los más chicos queden "arriba"
{
const svgContainer = d3.create("svg")
.attr("width", 256)
.attr("height", 256);

// Centro de los círculos
const centerXCoordinate = 128;
const centerYCoordinate = 128;

// Defino los radios a usar
const circleRadii = [105,70,35];

// Defindo los colores para cada radio
const circleColors = ["green", "purple", "red"];

// Agrego los círculos al contenedor
svgContainer.selectAll("circle")
.data(circleRadii)
.enter()
.append("circle")
.attr("cx", centerXCoordinate) //mismo centro para todos
.attr("cy", centerYCoordinate) //mismo centro para todos
.attr("r", d => d) // info de radios
.attr("fill", (d, index) => circleColors[index]); // info de colores

// 12. Return the root SVG node, which Observable will render in the notebook [8-13].
return svgContainer.node();
}
Insert cell
Insert cell
{
// parámetros de la grilla
// todos círculos iguales, dos gradientes de colores, izq a derecha, arriba a abajo, extremos negro, rojo, verde, amarillo
// el color de cada círculo depende de la posición x,y en la grilla
const width = 350;
const height = 350;
const cols = 17;
const rows = 17;
const radius = 10;
const padding = 5;

const svgContainer = d3.create("svg")
.attr("width", width)
.attr("height", height);

// Funcion que define el color según la posición x,y en la grilla
function getColor(xRatio, yRatio) {
// Define the start and end colors for the horizontal interpolation at the top and bottom.
const topStartColor = "black";
const topEndColor = "red";
const bottomStartColor = "green";
const bottomEndColor = "yellow";

// Defino la "posición" en el gradiente horizontal en funciuón de la posición horizontal en la grilla
const topColor = d3.interpolateRgb(topStartColor, topEndColor)(xRatio);
const bottomColor = d3.interpolateRgb(bottomStartColor, bottomEndColor)(xRatio);

// Luego determinar el color en base al gradiente horizontal, agrego el efecto del gradiente vertical en función de la posición y
return d3.interpolateRgb(topColor, bottomColor)(yRatio);
}

// Grilla. Cada ejecución del ciclo (cominado i, j) agrega un círculo a la grilla
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
// calculo los ratios x e y para poder usar la interpolación
const xRatio = i / (cols - 1);
const yRatio = j / (rows - 1);

// calculo la posición del centro del círculo i,j.
const cx = i * (radius * 2 + padding) + radius;
const cy = j * (radius * 2 + padding) + radius;

// Agrego el círculo al svg
svgContainer.append("circle")
.attr("cx", cx)
.attr("cy", cy)
.attr("r", radius)
.attr("fill", getColor(xRatio, yRatio)); // Set the fill color using the getColor function
}
}

// 5. Return the root SVG node.
return svgContainer.node();
}

Insert cell
Insert cell
// grilla de cícrculos rellenos negros cuyo diámetro depende de la distancia al centro

{
const width = 500;
const height = 500;
const cols = 17; // Número de columnas
const rows = 17; // Número de filas
const maxRadius = 12; // Tamaño máximo de los círculos

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);

// Centro del patrón
const centerX = (cols - 1) / 2;
const centerY = (rows - 1) / 2;
const maxDist = Math.sqrt(centerX ** 2 + centerY ** 2); // Distancia máxima al centro

// Escala de tamaño ajustada para mayor cambio en radios (el incremento del radio no es lineal)
const radiusScale = d3.scalePow().exponent(4)
.domain([0, 1])
.range([maxRadius * 0.1, maxRadius]);

// Dibujar la cuadrícula
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
const dist = Math.sqrt((i - centerX) ** 2 + (j - centerY) ** 2) / maxDist; // Normalizar distancia
const radius = radiusScale(1 - dist); // Cambio más rápido en tamaño

svg.append("circle")
.attr("cx", i * (width / cols))
.attr("cy", j * (height / rows))
.attr("r", radius)
.attr("fill", "black");
}
}

return svg.node();
}


Insert cell
Insert cell
// Grilla de triángulos equiláteros
// Cada fila repite el patrón de un triángulo azul "para arriba"y uno rojo "para abajo"
// Luego se repiten las filas

{
const numRows = 17;
const numTrianglesPerRow = 32; // Triángulos totales por fila
const triangleWidth = 20; // Base del triángulo
const triangleHeight = Math.sqrt(3) / 2 * triangleWidth; // Altura del triángulo
const horizontalSpacing = -6; // Espaciado horizontal entre triángulos
const verticalSpacing = 5; // Espaciado vertical entre triángulos
const svgHeight = numRows * (triangleHeight + verticalSpacing) + 10; // Alto del SVG
const svgWidth = numTrianglesPerRow * (triangleWidth + horizontalSpacing) - horizontalSpacing - 15; // Ancho del SVG

const svg = d3.create("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);

const rowData = [...Array(numRows).keys()]; // creo un array con el número de fila (0 a 16)
const triangleData = [...Array(numTrianglesPerRow).keys()]; // creo un array con posición dentro de la fila (0 a 31)

//creo todos los triángulos y luego los traslado a su posición. para cada triángulo necesito las coordenadas x.y de cada vértice (es un polígono)
const triangleUpPoints = `0,${-triangleHeight / 2} ${triangleWidth / 2},${triangleHeight / 2} ${-triangleWidth / 2},${triangleHeight / 2}`; //
const triangleDownPoints = `0,${triangleHeight / 2} ${triangleWidth / 2},${-triangleHeight / 2} ${-triangleWidth / 2},${-triangleHeight / 2}`;
// a cada fila le agrego la cantidad de triángulos determinada, alternando entre un triangulo y otro.
rowData.forEach((rowIndex) => {
const row = svg.append("g")
.attr("transform", `translate(0, ${rowIndex * (triangleHeight + verticalSpacing) + 5})`);

row.selectAll("polygon")
.data(triangleData)
.enter()
.append("polygon")
.attr("points", (d) => d % 2 === 0 ? triangleUpPoints : triangleDownPoints)
.attr("transform", (d) => `translate(${d * (triangleWidth + horizontalSpacing) + triangleWidth / 2 - 7}, ${triangleHeight / 2})`)
.style("fill", (d) => d % 2 === 0 ? "yellow" : "white")
.style("stroke", (d) => d % 2 === 0 ? "blue" : "red")
.style("stroke-width", 2);
});

return svg.node();
}

Insert cell
Insert cell
// fila de hexágonos
// cada fila aumenta el espesor de la línea
// según si la fila es par o impar cambia la posición inicial
{
// Parámetros base
const cols = 7;
const rows = 7;
const radius = 25;
const hexHeight = radius * 2;
const hexWidth = Math.sqrt(3) * radius;
const vertSpacing = hexHeight - 5; // separación vertical
const horizSpacing = hexWidth + 7; // separación horizontal
const svgWidth = cols * horizSpacing + 50;
const svgHeight = rows * vertSpacing + 50;

// Crear el SVG
const svg = d3.create("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);

// Generar la grilla
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const x = col * horizSpacing + (row % 2 === 1 ? horizSpacing / 2 : 0) + 25; // la posición inicial de la fila depende de si la fila es par o impar
const y = row * vertSpacing + 25;

svg.append("polygon")
.attr("points", hexagon(x, y, radius)) // llamo a la función
.style("fill", "none")
.style("stroke", "black")
.style("stroke-width", 1 + row * 0.6); // grosor de línea aumenta con la fila
}
}

return svg.node();
}

Insert cell
// Función para generar hexágonos en x,y de radio r.
function hexagon(x, y, r) {
var x1 = x;
var y1 = y - r;
var x2 = x + Math.cos(Math.PI / 6) * r;
var y2 = y - Math.sin(Math.PI / 6) * r;
var x3 = x + Math.cos(Math.PI / 6) * r;
var y3 = y + Math.sin(Math.PI / 6) * r;
var x4 = x;
var y4 = y + r;
var x5 = x - Math.cos(Math.PI / 6) * r;
var y5 = y + Math.sin(Math.PI / 6) * r;
var x6 = x - Math.cos(Math.PI / 6) * r;
var y6 = y - Math.sin(Math.PI / 6) * r;

var path = x1 + ',' + y1 + " " + x2 + ',' + y2 + " " + x3 + ',' + y3 + " " + x4 + ',' + y4 + " " + x5 + ',' + y5 + " " + x6 + ',' + y6;
return path;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more