modularize infill functionality
This commit is contained in:
parent
7ba1914300
commit
c8297f12ea
9 changed files with 174 additions and 129 deletions
|
|
@ -45,11 +45,12 @@ var gs_kiri_fdm = exports;
|
|||
update_start = time(),
|
||||
minSolid = spro.sliceSolidMinArea,
|
||||
solidLayers = spro.sliceSolidLayers,
|
||||
doSolidLayers = solidLayers && !spro.sliceVase,
|
||||
vaseMode = spro.sliceFillType === 'vase',
|
||||
doSolidLayers = solidLayers && !vaseMode,
|
||||
firstOffset = sdev.nozzleSize / 2,
|
||||
shellOffset = sdev.nozzleSize * spro.sliceShellSpacing,
|
||||
fillOffset = shellOffset * settings.synth.fillOffsetMult,
|
||||
fillSpacing = sdev.nozzleSize * spro.sliceFillSpacing,
|
||||
fillSpacing = sdev.nozzleSize,
|
||||
sliceFillAngle = spro.sliceFillAngle,
|
||||
view = widget.mesh && widget.mesh.newGroup ? widget.mesh.newGroup() : null;
|
||||
|
||||
|
|
@ -111,9 +112,9 @@ var gs_kiri_fdm = exports;
|
|||
slice.index < spro.sliceBottomLayers ||
|
||||
slice.index > slices.length - spro.sliceTopLayers-1 ||
|
||||
spro.sliceFillSparse > 0.95
|
||||
) && !spro.sliceVase;
|
||||
) && !vaseMode;
|
||||
slice.doShells(spro.sliceShells, firstOffset, shellOffset, fillOffset, {
|
||||
vase: spro.sliceVase,
|
||||
vase: vaseMode,
|
||||
thin: false && spro.detectThinWalls
|
||||
});
|
||||
if (solid) slice.doSolidLayerFill(fillSpacing, sliceFillAngle);
|
||||
|
|
@ -147,14 +148,14 @@ var gs_kiri_fdm = exports;
|
|||
}
|
||||
|
||||
// sparse layers only present when non-vase mose and sparse % > 0
|
||||
if (!spro.sliceVase && spro.sliceFillSparse > 0.0) {
|
||||
if (!vaseMode && spro.sliceFillSparse > 0.0) {
|
||||
forSlices(0.8, 1.0, function(slice) {
|
||||
slice.doSparseLayerFill({
|
||||
spacing: fillSpacing,
|
||||
density: spro.sliceFillSparse,
|
||||
bounds: widget.getBoundingBox(),
|
||||
height: spro.sliceHeight,
|
||||
type: spro.sliceFillGyroid ? 'gyroid' : 'hex'
|
||||
type: spro.sliceFillType
|
||||
});
|
||||
}, "infill");
|
||||
}
|
||||
|
|
|
|||
121
js/kiri-fill.js
Normal file
121
js/kiri-fill.js
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
||||
|
||||
"use strict";
|
||||
|
||||
var gs_kiri_fill = exports;
|
||||
|
||||
(function() {
|
||||
|
||||
if (!self.kiri) self.kiri = {};
|
||||
if (self.kiri.fill) return;
|
||||
|
||||
var KIRI = self.kiri,
|
||||
BASE = self.base,
|
||||
UTIL = BASE.util,
|
||||
ROUND = UTIL.round,
|
||||
DEG2RAD = Math.PI / 180,
|
||||
FILL = self.kiri.fill = {
|
||||
hex: sparseFillHexFull,
|
||||
gyroid: sparseFillGyroid
|
||||
};
|
||||
|
||||
function sparseFillHexFull(target) {
|
||||
sparseFillHex(target, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* emitter creates a hex infill pattern and sends to target
|
||||
*
|
||||
* @param {Object} target
|
||||
* @param {boolean} full continuous walls
|
||||
*/
|
||||
function sparseFillHex(target, full) {
|
||||
// compute segment lengths (vert/horiz and 45)
|
||||
var spacing = target.offset() / 2,
|
||||
vhlen = (1 - target.density()) * 4 + spacing,
|
||||
anxlen = ROUND(Math.cos(30 * DEG2RAD) * vhlen, 7),
|
||||
anylen = ROUND(Math.sin(30 * DEG2RAD) * vhlen, 7),
|
||||
bounds = target.bounds(),
|
||||
even = true,
|
||||
evenZ = target.zIndex() % 2 === 0,
|
||||
maxy = bounds.max.y + (vhlen + anylen * 2),
|
||||
x, y;
|
||||
|
||||
if (full || evenZ) {
|
||||
x = bounds.min.x;
|
||||
for (;;) {
|
||||
if (even && x > bounds.max.x) break;
|
||||
if (!even && x > bounds.max.x + anxlen + spacing) break;
|
||||
y = bounds.min.y;
|
||||
target.newline();
|
||||
while (y <= maxy) {
|
||||
target.emit(x,y);
|
||||
y += vhlen;
|
||||
target.emit(x,y);
|
||||
if (even) x += anxlen; else x -= anxlen;
|
||||
y += anylen;
|
||||
target.emit(x,y);
|
||||
y += vhlen;
|
||||
target.emit(x,y);
|
||||
if (even) x -= anxlen; else x += anxlen;
|
||||
y += anylen;
|
||||
}
|
||||
x += spacing;
|
||||
if (even) x += (anxlen * 2);
|
||||
even = !even;
|
||||
target.newline();
|
||||
}
|
||||
} else {
|
||||
y = bounds.min.y + vhlen;
|
||||
for (;;) {
|
||||
if (even && y > bounds.max.y) break;
|
||||
if (!even && y > bounds.max.y + anylen) break;
|
||||
x = bounds.min.x;
|
||||
target.newline();
|
||||
while (x < bounds.max.x) {
|
||||
target.emit(x,y);
|
||||
if (even) y += anylen; else y -= anylen;
|
||||
x += anxlen;
|
||||
target.emit(x,y);
|
||||
x += spacing;
|
||||
target.emit(x,y);
|
||||
if (even) y -= anylen; else y += anylen;
|
||||
x += anxlen;
|
||||
target.emit(x,y);
|
||||
x += spacing;
|
||||
}
|
||||
y += vhlen;
|
||||
if (even) y += (anylen * 2);
|
||||
even = !even;
|
||||
target.newline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sparseFillGyroid(target) {
|
||||
let bounds = target.bounds();
|
||||
let height = target.zHeight();
|
||||
let span_x = bounds.max.x - bounds.min.x;
|
||||
let span_y = bounds.max.y - bounds.min.y;
|
||||
let density = target.density();
|
||||
let tile = 1 + (1 - density) * 15;
|
||||
let tile_x = span_x / tile;
|
||||
let tile_y = span_y / tile;
|
||||
let tile_z = 1 / tile;
|
||||
let gyroid = BASE.gyroid.slice(target.zValue() * tile_z, (1 - density) * 500);
|
||||
|
||||
gyroid.polys.forEach(poly => {
|
||||
for (let tx=0; tx<=tile_x; tx++) {
|
||||
for (let ty=0; ty<=tile_y; ty++) {
|
||||
target.newline();
|
||||
let bx = tx * tile + bounds.min.x;
|
||||
let by = ty * tile + bounds.min.y;
|
||||
poly.forEach(point => {
|
||||
target.emit(bx + point.x * tile, by + point.y * tile);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
103
js/kiri-slice.js
103
js/kiri-slice.js
|
|
@ -10,16 +10,15 @@ var gs_kiri_slice = exports;
|
|||
if (self.kiri.Slice) return;
|
||||
|
||||
var KIRI = self.kiri,
|
||||
FILL = KIRI.fill,
|
||||
BASE = self.base,
|
||||
UTIL = BASE.util,
|
||||
POLY = BASE.polygons,
|
||||
PRO = Slice.prototype,
|
||||
fillArea = POLY.fillArea,
|
||||
newPoint = BASE.newPoint,
|
||||
ROUND = UTIL.round,
|
||||
MIN = Math.min,
|
||||
MAX = Math.max,
|
||||
DEG2RAD = Math.PI / 180,
|
||||
NOKEY = BASE.key.NONE,
|
||||
outline_colors = [0xffff00, 0x00ffff, 0xff00ff, 0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0x000000],
|
||||
trace_color = 0x000000,
|
||||
|
|
@ -636,9 +635,8 @@ var gs_kiri_slice = exports;
|
|||
scope.isSparseFill = true;
|
||||
|
||||
// use specified fill type
|
||||
switch (type) {
|
||||
case 'hex': sparseFillHex(target, true); break;
|
||||
case 'gyroid': sparseFillGyroid(target); break;
|
||||
if (type && FILL[type]) {
|
||||
FILL[type](target);
|
||||
}
|
||||
|
||||
// force emit of last line
|
||||
|
|
@ -1219,101 +1217,6 @@ var gs_kiri_slice = exports;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* emitter creates a hex infill pattern and sends to target
|
||||
*
|
||||
* @param {Object} target
|
||||
* @param {boolean} full continuous walls
|
||||
*/
|
||||
function sparseFillHex(target, full) {
|
||||
// compute segment lengths (vert/horiz and 45)
|
||||
var spacing = target.offset() / 2,
|
||||
vhlen = (1 - target.density()) * 4 + spacing,
|
||||
anxlen = ROUND(Math.cos(30 * DEG2RAD) * vhlen, 7),
|
||||
anylen = ROUND(Math.sin(30 * DEG2RAD) * vhlen, 7),
|
||||
bounds = target.bounds(),
|
||||
even = true,
|
||||
evenZ = target.zIndex() % 2 === 0,
|
||||
maxy = bounds.max.y + (vhlen + anylen * 2),
|
||||
x, y;
|
||||
|
||||
if (full || evenZ) {
|
||||
x = bounds.min.x;
|
||||
for (;;) {
|
||||
if (even && x > bounds.max.x) break;
|
||||
if (!even && x > bounds.max.x + anxlen + spacing) break;
|
||||
y = bounds.min.y;
|
||||
target.newline();
|
||||
while (y <= maxy) {
|
||||
target.emit(x,y);
|
||||
y += vhlen;
|
||||
target.emit(x,y);
|
||||
if (even) x += anxlen; else x -= anxlen;
|
||||
y += anylen;
|
||||
target.emit(x,y);
|
||||
y += vhlen;
|
||||
target.emit(x,y);
|
||||
if (even) x -= anxlen; else x += anxlen;
|
||||
y += anylen;
|
||||
}
|
||||
x += spacing;
|
||||
if (even) x += (anxlen * 2);
|
||||
even = !even;
|
||||
target.newline();
|
||||
}
|
||||
} else {
|
||||
y = bounds.min.y + vhlen;
|
||||
for (;;) {
|
||||
if (even && y > bounds.max.y) break;
|
||||
if (!even && y > bounds.max.y + anylen) break;
|
||||
x = bounds.min.x;
|
||||
target.newline();
|
||||
while (x < bounds.max.x) {
|
||||
target.emit(x,y);
|
||||
if (even) y += anylen; else y -= anylen;
|
||||
x += anxlen;
|
||||
target.emit(x,y);
|
||||
x += spacing;
|
||||
target.emit(x,y);
|
||||
if (even) y -= anylen; else y += anylen;
|
||||
x += anxlen;
|
||||
target.emit(x,y);
|
||||
x += spacing;
|
||||
}
|
||||
y += vhlen;
|
||||
if (even) y += (anylen * 2);
|
||||
even = !even;
|
||||
target.newline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sparseFillGyroid(target) {
|
||||
let bounds = target.bounds();
|
||||
let height = target.zHeight();
|
||||
let span_x = bounds.max.x - bounds.min.x;
|
||||
let span_y = bounds.max.y - bounds.min.y;
|
||||
let density = target.density();
|
||||
let tile = 1 + (1 - density) * 15;
|
||||
let tile_x = span_x / tile;
|
||||
let tile_y = span_y / tile;
|
||||
let tile_z = 1 / tile;
|
||||
let gyroid = BASE.gyroid.slice(target.zValue() * tile_z, (1 - density) * 500);
|
||||
|
||||
gyroid.polys.forEach(poly => {
|
||||
for (let tx=0; tx<=tile_x; tx++) {
|
||||
for (let ty=0; ty<=tile_y; ty++) {
|
||||
target.newline();
|
||||
let bx = tx * tile + bounds.min.x;
|
||||
let by = ty * tile + bounds.min.y;
|
||||
poly.forEach(point => {
|
||||
target.emit(bx + point.x * tile, by + point.y * tile);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function newTop(poly) {
|
||||
return new Top(poly);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,8 +154,8 @@ if (self.window) {
|
|||
[
|
||||
"license","ext-n3d","ext-clip","add-array",
|
||||
"add-three","geo","geo-point","geo-debug","geo-bounds",
|
||||
"geo-line","geo-slope","geo-polygon","geo-polygons", "geo-gyroid",
|
||||
"kiri-slice","kiri-slicer","kiri-driver-fdm","kiri-driver-cam",
|
||||
"geo-line","geo-slope","geo-polygon","geo-polygons","geo-gyroid",
|
||||
"kiri-fill","kiri-slice","kiri-slicer","kiri-driver-fdm","kiri-driver-cam",
|
||||
"kiri-driver-laser","kiri-widget","kiri-pack","kiri-print","kiri-codec"
|
||||
].forEach(function(scr) {
|
||||
importScripts(["/js/",scr,".js","/v"+ver].join(''));
|
||||
|
|
|
|||
53
js/kiri.js
53
js/kiri.js
|
|
@ -58,9 +58,8 @@ self.kiri.license = exports.LICENSE;
|
|||
sliceShellSpacing: 1,
|
||||
sliceFillAngle: 1,
|
||||
sliceFillOverlap: 1,
|
||||
sliceFillSpacing: 1,
|
||||
sliceFillSparse: 1,
|
||||
sliceFillGyroid: 1,
|
||||
sliceFillType: 1,
|
||||
sliceSupportEnable: 1,
|
||||
sliceSupportDensity: 1,
|
||||
sliceSupportOffset: 1,
|
||||
|
|
@ -73,7 +72,6 @@ self.kiri.license = exports.LICENSE;
|
|||
sliceSolidLayers: 1,
|
||||
sliceBottomLayers: 1,
|
||||
sliceTopLayers: 1,
|
||||
sliceVase: 1,
|
||||
firstSliceHeight: 1,
|
||||
firstLayerRate: 1,
|
||||
firstLayerFillRate: 1,
|
||||
|
|
@ -202,6 +200,17 @@ self.kiri.license = exports.LICENSE;
|
|||
},
|
||||
// --------------- (default)
|
||||
settings = {
|
||||
infill:[
|
||||
{
|
||||
name: "vase"
|
||||
},
|
||||
{
|
||||
name: "hex"
|
||||
},
|
||||
{
|
||||
name: "gyroid"
|
||||
}
|
||||
],
|
||||
// CAM only
|
||||
tools:[
|
||||
{
|
||||
|
|
@ -272,9 +281,8 @@ self.kiri.license = exports.LICENSE;
|
|||
sliceShellSpacing: 1.0,
|
||||
sliceFillAngle: 45,
|
||||
sliceFillOverlap: 0.3,
|
||||
sliceFillSpacing: 1.0,
|
||||
sliceFillSparse: 0.5,
|
||||
sliceFillGyroid: false,
|
||||
sliceFillType: "hex",
|
||||
|
||||
sliceSupportEnable: false,
|
||||
sliceSupportDensity: 0.25,
|
||||
|
|
@ -289,7 +297,6 @@ self.kiri.license = exports.LICENSE;
|
|||
sliceSolidLayers: 3,
|
||||
sliceBottomLayers: 3,
|
||||
sliceTopLayers: 3,
|
||||
sliceVase: false,
|
||||
|
||||
firstSliceHeight: 0.25,
|
||||
firstLayerRate: 30,
|
||||
|
|
@ -1707,6 +1714,7 @@ self.kiri.license = exports.LICENSE;
|
|||
var key, val;
|
||||
|
||||
fillMissingSettings(settingsDefault, settings);
|
||||
settings.infill = settingsDefault.infill;
|
||||
|
||||
for (key in scope) {
|
||||
if (!scope.hasOwnProperty(key)) continue;
|
||||
|
|
@ -1721,11 +1729,16 @@ self.kiri.license = exports.LICENSE;
|
|||
} else if (typ === 'select-one') {
|
||||
uie.innerHTML = '<option></option>';
|
||||
var chosen = null;
|
||||
settings.tools.forEach(function(tool,index) {
|
||||
if (val === tool.id) chosen = index+1;
|
||||
var source = uie.parentNode.getAttribute('source');
|
||||
var list = settings[source];
|
||||
list.forEach(function(tool, index) {
|
||||
let id = tool.id || tool.name;
|
||||
if (val === id) {
|
||||
chosen = index + 1;
|
||||
}
|
||||
var opt = DOC.createElement('option');
|
||||
opt.appendChild(DOC.createTextNode(tool.name));
|
||||
opt.setAttribute('value', tool.id);
|
||||
opt.setAttribute('value', id);
|
||||
uie.appendChild(opt);
|
||||
});
|
||||
if (chosen) uie.selectedIndex = chosen;
|
||||
|
|
@ -1750,7 +1763,8 @@ self.kiri.license = exports.LICENSE;
|
|||
if (!scope.hasOwnProperty(key)) continue;
|
||||
if (UI.hasOwnProperty(key)) {
|
||||
var nval = null,
|
||||
uie = UI[key];
|
||||
uie = UI[key],
|
||||
src = uie.parentNode.getAttribute('source');
|
||||
// skip empty UI values
|
||||
if (!uie || uie === '') continue;
|
||||
if (uie.type === 'text') {
|
||||
|
|
@ -1759,7 +1773,10 @@ self.kiri.license = exports.LICENSE;
|
|||
nval = UI[key].checked;
|
||||
} else if (uie.type === 'select-one') {
|
||||
if (uie.selectedIndex > 0) {
|
||||
nval = parseInt(uie.options[uie.selectedIndex].value);
|
||||
nval = uie.options[uie.selectedIndex].value;
|
||||
if (src === 'tools') {
|
||||
nval = parseInt(nval);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scope[key] != nval) {
|
||||
|
|
@ -2479,22 +2496,22 @@ self.kiri.license = exports.LICENSE;
|
|||
bedWidth: UC.newInput("width", {title:"millimeters", convert:UC.toInt, modes:LASER}),
|
||||
bedDepth: UC.newInput("depth", {title:"millimeters", convert:UC.toInt, modes:LASER}),
|
||||
|
||||
process: UC.newGroup("process", control, {modes:FDM_LASER}),
|
||||
process: UC.newGroup("slicing", control, {modes:FDM_LASER}),
|
||||
|
||||
// 3d print
|
||||
sliceHeight: UC.newInput("layer height", {title:"millimeters", convert:UC.toFloat, modes:FDM}),
|
||||
sliceShells: UC.newInput("shell count", {convert:UC.toInt, modes:FDM}),
|
||||
sliceShellSpacing: UC.newInput("shell spacing", {title:"as a percentage of nozzle width\n< 1.0 causes shell overlap\nrecommended 0.85 - 1.0", convert:UC.toFloat, bound:UC.bound(0.5,1.0), modes:FDM}),
|
||||
sliceFillOverlap: UC.newInput("fill overlap", {title:"overlap with shell\nas % of nozzle width\nhigher bonds better\n0.0 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
|
||||
sliceFillSpacing: UC.newInput("fill spacing", {title:"for solid fill areas\nas a percentage of nozzle width\n< 1.0 causes fill overlap\nrecommended 0.85 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
|
||||
sliceFillAngle: UC.newInput("fill angle", {title:"base angle in degrees", convert:UC.toFloat, modes:FDM}),
|
||||
sliceFillSparse: UC.newInput("fill ratio", {title:"for infill areas\n0.0 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,1.0), modes:FDM}),
|
||||
sliceSolidMinArea: UC.newInput("solid area", {title:"minimum area (mm^2)\nrequired to keep solid\nmust be > 0.1", convert:UC.toFloat, modes:FDM}),
|
||||
sliceSolidLayers: UC.newInput("solid layers", {title:"flat area fill projections\nbased on layer deltas", convert:UC.toInt, modes:FDM}),
|
||||
sliceBottomLayers: UC.newInput("base layers", {title:"bottom solid layer count", convert:UC.toInt, modes:FDM}),
|
||||
sliceTopLayers: UC.newInput("top layers", {title:"top solid layer count", convert:UC.toInt, modes:FDM}),
|
||||
sliceVase: UC.newBoolean("vase mode", onBooleanClick, {modes:FDM}),
|
||||
sliceFillGyroid: UC.newBoolean("gyroid infill", onBooleanClick, {title: "best with fine layers\nand large interior voids", modes:FDM}),
|
||||
|
||||
process: UC.newGroup("fill", control, {modes:FDM}),
|
||||
sliceFillOverlap: UC.newInput("shell overlap", {title:"overlap with shell\nas % of nozzle width\nhigher bonds better\n0.0 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
|
||||
sliceFillAngle: UC.newInput("solid angle", {title:"base angle in degrees", convert:UC.toFloat, modes:FDM}),
|
||||
sliceFillSparse: UC.newInput("percentage", {title:"for infill areas\n0.0 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,1.0), modes:FDM}),
|
||||
sliceFillType: UC.newSelectField("type", {modes:FDM}, "infill"),
|
||||
|
||||
// laser
|
||||
laserOffset: UC.newInput("cut offset", {title:"millimeters\nadjust for beam width", convert:UC.toFloat, modes:LASER}),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
var exports = {
|
||||
COPYRIGHT:"Copyright (C) 2014-2019 Stewart Allen <sa@grid.space> - All Rights Reserved",
|
||||
LICENSE:"See the license.md file included with the source distribution",
|
||||
VERSION:"1.3.3"
|
||||
VERSION:"1.4.0"
|
||||
};
|
||||
if (!module) var module = {};
|
||||
module.exports = exports;
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ var gs_moto_ui = exports;
|
|||
return ip;
|
||||
}
|
||||
|
||||
function newSelectField(label, options) {
|
||||
function newSelectField(label, options, source) {
|
||||
var row = newDiv(options),
|
||||
ip = DOC.createElement('select'),
|
||||
hide = options && options.hide,
|
||||
|
|
@ -299,6 +299,7 @@ var gs_moto_ui = exports;
|
|||
lastDiv.appendChild(row);
|
||||
row.appendChild(newLabel(label));
|
||||
row.appendChild(ip);
|
||||
row.setAttribute("source", source || "tools");
|
||||
row.setAttribute("class", ["flow-row",lastGroup].join(" "));
|
||||
row.style.display = hide ? 'none' : '';
|
||||
if (options) {
|
||||
|
|
|
|||
|
|
@ -759,6 +759,7 @@ var ver = require('../js/license.js'),
|
|||
"moto-db",
|
||||
"moto-ui",
|
||||
"kiri-lang",
|
||||
"kiri-fill",
|
||||
"kiri-db",
|
||||
"kiri-slice",
|
||||
"kiri-slicer",
|
||||
|
|
@ -805,6 +806,7 @@ var ver = require('../js/license.js'),
|
|||
"geo-polygons",
|
||||
"geo-gyroid",
|
||||
"kiri-lang",
|
||||
"kiri-fill",
|
||||
"kiri-slice",
|
||||
"kiri-slicer",
|
||||
"kiri-driver-fdm",
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ button.selected {
|
|||
flex-grow: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: white;
|
||||
color: black;
|
||||
border: 0;
|
||||
margin-bottom: 2px;
|
||||
padding-bottom: 2px;
|
||||
|
|
|
|||
Loading…
Reference in a new issue