JSFiddle - React, Tailwind, and code Playground
by Gwyn Milcote
HTML
Trying to use SVG to generate blobby shapes, to use as pied (or tortoiseshell) markings on griffin coat. They must use seed/slope/intercept values, so the same 3 numbers always results in same shape, when an individual griffin is loaded.<p>So far so good, but how to apply the shapes to coat area? The griffin below is brown, and should be partially covered in a patchy white layer. The white 'markings' mustn't extend outside the griffin outline. Two options: have a solid white griffin-shape and use the SVG to cut areas out of it, or use it as a mask to cut out excess areas of the SVG? I can't get either to work.</p>
Seed: <input id='current_seed' type='number' min='1' max='10000' value='123'> |
Slope: <input id='current_slope' type='number' min='800' max='1200' value='1000'> |
Intercept: <input id='current_intercept' type='number' min='400' max='800' value='600'>
<p><button onclick="submitSeed()">Submit</button>
<button onclick="randomSeed()">Randomise!</button></p>
<svg viewBox="0 0 600 600">
<!-- Simple circle test. Works. -->
<mask id="circle">
<circle id="outer" cx="250" cy="150" r="100" fill="white"/>
<circle id="inner" cx="250" cy="150" r="50"/>
</mask>
<defs>
<filter id="pied" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
<!-- seed, zoom, octaves -->
<feTurbulence id="turbulence" type="fractalNoise" seed="123" baseFrequency="0.004" numOctaves="5" result="noise">
</feTurbulence>
<!-- water level -->
<feComponentTransfer in="noise" result="water_level">
<feFuncR id="water_level" type="linear" intercept="600" slope="-1000">
</feFuncR>
</feComponentTransfer>
<!-- black area -->
<feColorMatrix...
CSS
body {
background-color: #ccc;
}
input {
max-width: 80px;
}
#griffin_coat {
width: 500px;
height: 400px;
margin: 15px;
position: relative;
}
#griffin_coat img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.brown_filter {
filter: invert(35%) sepia(78%) saturate(313%) hue-rotate(353deg) brightness(97%) contrast(90%);
}
.white_filter {
filter: invert(100%);
}
/* Tests. */
.circle_mask {
-webkit-mask-image: url("#circle");
mask-image: url("#circle");
}
.pattern_mask {
-webkit-mask-image: url("#pattern");
mask-image: url("#pattern");
}
/* This is the final, desired class. Patchy marks. */
.pied_mask {
-webkit-mask-image: url("#pied");
mask-image: url("#pied");
}
/*
Trying opposite approach.
Cutting out images to fill griffin shape only.
*/
.griffin_shape {
-webkit-mask-image: url("http://mythstable.epizy.com/ootg/f_adult/base.png");
mask-image: url("http://mythstable.epizy.com/ootg/f_adult/base.png");
}
.umm {
mask-image: url("#umm");
}
.item {
mask-image: url(https://img-fotki.yandex.ru/get/9492/5091629.9d/0_80e74_9f303e3d_M.png);
mask-repeat: space;
}
JavaScript
// Use random values.
function randomSeed(){
var seed = Math.round(Math.random() * 10000);
var slope = randomNumber(800, 1200);
var intercept = randomNumber(400, 600);
document.getElementById("current_seed").value = seed;
document.getElementById("current_slope").value = slope;
document.getElementById("current_intercept").value = intercept;
generateMap(seed, slope, intercept);
}
// Use custom values, currently written in the inputs.
function submitSeed(){
var seed = document.getElementById("current_seed").value;
var slope = document.getElementById("current_slope").value;
var intercept = document.getElementById("current_intercept").value;
generateMap(seed, slope, intercept);
}
// Update the SVG.
function generateMap(seed, slope, intercept){
document.getElementById("turbulence").setAttribute("seed", seed);
document.getElementById("water_level").setAttribute("slope", 0 - slope);
document.getElementById("water_level").setAttribute("intercept", intercept);
}
// Pick a random number between any two.
function randomNumber(n1, n2){
if(n1 == n2){
return n1;
}
var min = n1;
var max = n2;
if(n1 > n2){
min = n2;
max = n1;
}
return Math.floor(Math.random()*(max-min+1)+min);
}