evenodd fill on a group of shapes - svg

How would I get an evenodd fill on #g3:
<defs>
<ellipse id="g1" cx="100" cy="100" rx="75" ry="40" fill="none" />
</defs>
<g id="g3" stroke="black" stroke-width="2">
<g id="g2">
<use xlink:href="#g1" />
<use xlink:href="#g1" transform="rotate(30 100 100)"/>
</g>
<use xlink:href="#g2" transform="rotate(60 100 100)"/>
<use xlink:href="#g2" transform="rotate(120 100 100)"/>
</g>
So iow, its just a series of rotated ellipses, but I want the intersecting regions evenodd filled:
Just adding fill-rule = "evenodd" to #g3 doesn't do it. Sorry if this is too elementary. I realize there's probably a way to do this in .js, but want to know if it can be done with straight svg.
For example, is there a way in which #g3 itself is defined as a new path in some sense, so that evenodd could be applied to it in totality.

I guess just basically draw a series of arcs in one path in a loop from .js - that way its all one path and evenodd fill-type should work, I guess. Nevermind.

Related

How to make SVG stroke-width consistent across the entire path?

Here is an example:
<svg width="60" height="30">
<path stroke="red" stroke-width="1" fill="none" d="M0 25 L10 25 L45 5 L55 5"/>
</svg>
I find that the inclined line segment has a thinner stroke-width then the horizontal lines. How do I make the stroke-width consistent across the entire path?
Breaking the path into multiple paths did not help as the line joins are not elegant. The points are generated dynamically so the solution needs to be generic.
<svg width="180" height="120">
<g stroke="red" fill="none">
<g stroke-width="1">
<path id="p" d="M0 25l10 0l35-20l10 0" />
<use href="#p" y="25" shape-rendering="optimizeQuality"/>
<use href="#p" y="50" shape-rendering="crispEdges"/>
</g>
<use href="#p" stroke-width="2" x="60"/>
<use href="#p" stroke-width="3" x="120"/>
</g>
</svg>
I added stroke-width 2 and 3 to your example. The effect is less because more pixels are being used to draw the line.
At 1 pixel your monitor and Browser can't make much of it. Only add (some) anti-aliasing.
screenshot zoomed:
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering won't help
because it is still your monitor/browser only having 1 pixel to draw that line
Same for CSS https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering..
it is still 1 pixel
Same for setting style="transform:translateZ(0)"
Maybe in the future with Quantum Dot display technology it will look better. https://en.wikipedia.org/wiki/Next_generation_of_display_technology

position of text relative to another element in SVG

I think I'm doing this the hard way... I have ~200 circles that need to have text in the middle of them (atomic structures in svg). What I've been doing is offsetting the next by typing in the absolute positional value of each text offset by 5.2 from the circle it's in,
<circle id="H4_11_" class="st3" cx="1660.8" cy="714.5" r="10"/>
<use xlink:href="#hydrogen_label" transform="matrix(1 0 0 1 1655.6 719.7)" />
Is there a way to just position the text relative to the first one by placing each circle in it's own container? Something like,
<g>
<circle id="H4_11_" class="st3" cx="1660.8" cy="714.5" r="10"/>
<use xlink:href="#hydrogen_label" transform="(+5.2,-5.2)" />
</g>
Tried the above and it didn't work.
You could restructure things like this perhaps so that the local co-ordinate system is set in the <g>.
<g transform="translate(1660.8, 714.5)">
<circle id="H4_11_" class="st3" r="10"/>
<use xlink:href="#hydrogen_label" transform="translate(-5.2, 5.2)" />
</g>

How to color a region i.e the intersection of two object?

I want to color an intersection of two object, let's say a circle and a rectangle.
What I have tried so far is to defined a path bounding that region then add the fill attribute, but it seems too complicated.
Is there any other ways to do so?
Let me elabotare more one the problem:
<svg width="352" height="57" xmlns="http://www.w3.org/2000/svg">
<line y2="0.75" x2="103.95" y1="43.15" x1="42.35" stroke-width="1.5" stroke="#000" fill="none"/>
<line y2="50.35" x2="201.55" y1="0.75" x1="103.95" stroke-width="1.5" stroke="#000" fill="none"/>
<line y2="19.15" x2="239.95" y1="49.55" x1="201.55" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="none"/>
<line y2="55.95" x2="282.35" y1="18.35" x1="240.75" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="none"/>
<line y2="37.55" x2="351.15" y1="31.95" x1="0.75" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="none"/>
</svg>
I have a set of lines which the end of one is another end of the other.
I have another line intersecing these line.
How could I color the region i.e the triangles formed by these line?
You can use <clipPath> to clip the rect with the circle like so:
svg{width:100vh;}
<svg viewBox="50 50 200 100">
<clipPath id="clip">
<use xlink:href="#c" />
</clipPath>
<g fill="none" stroke="black" >
<circle id="c" cx="100" cy="100" r="30" />
<rect id="r" x="90" y="80" width="80" height="60" />
</g>
<use xlink:href="#r" clip-path="url(#clip)" fill="gold" />
</svg>
You could work with opacity to let the color of an overlapped element shine through like shown here:
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg">
<g style="stroke:#000; stroke-width:1">
<circle cx="50" cy="50" r="50" opacity="0.5" style="fill:red" />
<circle cx="100" cy="50" r="50" opacity="0.5" style="fill:yellow" />
<rect x="0" y="70" width="150" height="50" fill-opacity="0.5" style="fill:blue" />
</g>
</svg>
You can also specify the opacity levels separately for stroke and fill by using stroke-opacity and fill-opacity.
Edit:
Looking at your edited question again: when you are dealing with a succession of lines (and curves) you should use <path> elements instead of individual <line> elements. One of its advantages is that it comes with a fill behaviour that is almost exactly as you want it to be. However, to suit the requirements of your example you would need to find the positions where the straight line enters and leaves the zig-zag shape. These positions then define the points used for your filled path. The "original" (non-filled) path is then plotted over the filled path:
<svg width="352" height="57" xmlns="http://www.w3.org/2000/svg">
<path d="M56,33 103.95,0.75 201.55,50.35 239.95,19.15 260,36z
M0.75,31.95 351.15,37.55" style="fill:yellow" />
<path d="M42.35,43.15 103.95,0.75 201.55,50.35 239.95,19.15 282.35,55.95
M0.75,31.95 351.15,37.55" style="stroke:black;fill:none"/>
</svg>

Coordinate Origin for SVG Symbol

I would like to move a particular composite shape to a <Symbol> definition, and then re-use it. This helps make the SVG code neater, and provides some global control over the actual structure of that shape. The shape is symmetrical about a given mid-point; however, the x/y coordinates of subsequent <Use> statements need to reference the top-left corner rather than its natural original, and this means that all usage of the shape must be aware of its total size. Is there a way to position a Symbol by some origin other than its top-left corner?
Contrived example, purely to explain this better. The following concentric-circle Symbol has a natural origin. However, the subsequent Use statement has to offset its x/y coordinates by half the Symbol size in order to position it correctly (at 20,20 in this example). Ideally, the usage of the symbol should not have to know this information.
<defs>
<symbol id="ex">
<circle fill="green" cx="8" cy="8" r="8"/>
<circle fill="white" cx="8" cy="8" r="6"/>
<circle fill="green" cx="8" cy="8" r="4"/>
</symbol>
</defs>
<use xlink:href="#ex" x="12" y="12">
The downside to a symbol is that it cuts of rendering at the border of a viewport. (which is also an upside, since you can define a viewBox.) But you can avoid using it at all. Everything in a <defs> element is not rendered directly, so you can exchange the <symbol> for a <g> and center everything on the origin:
<defs>
<g id="ex">
<circle fill="green" cx="0" cy="0" r="8"/>
<circle fill="white" cx="0" cy="0" r="6"/>
<circle fill="green" cx="0" cy="0" r="4"/>
</g>
</defs>
<use xlink:href="#ex" x="12" y="12">
http://jsfiddle.net/nuzwn07n/
If I read your question correctly, you want to be able to say
<use xlink:href="#ex" x="20" y="20">
..and then the circle symbol is positioned with its center at [20,20]. The solution I propose requires you to know the size of the symbol, but only once (in the <symbol> declaration), and not on every <use> element.
1: In the <symbol>, put everything in a group which you translate so the center of the graphics lies on the top-left corner.
<symbol id="ex">
<g transform="translate(-8,-8)">
<circle ...
</g>
</symbol>
2: If you now <use> that symbol, you'll only see the quarter circle that's still within the symbol's "viewport". To display the whole circle, simply apply overflow="visible" to the <symbol>.
<symbol id="ex" overflow="visible">
<g transform="translate(-8,-8)">
<circle ...
</g>
</symbol>
http://jsfiddle.net/0ghucsrp/

SVG clipPath to clip the *outer* content out

Normally, the <clipPath> element hides everything that is outside the clip path. To achieve the opposite effect - that is to "cut out" something from the image - i want to use two paths in the clipPath and the clip-rule="evenodd" attribute. Basically, I want to "xor" the clip paths.
But it doesn't work. It shows the region "ORed":
<clipPath clip-rule="evenodd" id="imageclippath" clipPathUnits = "objectBoundingBox">
<rect clip-rule="evenodd" x="0.3" y="0.3" height="0.6" width="6" />
<rect clip-rule="evenodd" x="0" y="0" height="0.5" width="0.5" />
</clipPath>
<rect clip-path="url(#imageclippath)" x="0" y="0" height="500" width="500" fill="red"/>
EDIT:
My problem is that AFAIK <mask> doesn't work in iOS WebKit.
It's much easier to do what you're after with a mask, see this example. Here's the mask definition:
<mask id="maskedtext">
<circle cx="50%" cy="50%" r="50" fill="white"/>
<text x="50%" y="55%" text-anchor="middle" font-size="48">ABC</text>
</mask>
Regions that are white inside the mask will be kept, everything else will be clipped away.
Here's another example that uses clipPath instead, is a bit trickier since you need to use a path element to get the clip-rule to apply. The clipPath that uses clip-rule there looks like this:
<clipPath id="clipPath1">
<path id="p1" d="M10 10l100 0 0 100 -100 0ZM50 50l40 0 0 40 -40 0Z" clip-rule="evenodd"/>
</clipPath>

Resources