3

Given an SVG such as this fish bowl, I'm trying to calculate the volume of the area defined in pink as a percentage of the area between the "fill level" and "empty level".

I can't do a a simple percentage from top to bottom, as the fish bowl is irregularly shaped, and this will throw off the calculation by at least a few percentage points. I need to do this for many fish bowls of different shapes, so an algorithm is needed to determine the volume of each bowl.

Is there any way I can do this with javascript on an SVG element, and if so, is there any way I can go about figuring this out within element areas as a percentage?

enter image description here

Update: Uploaded sample SVG to jsfiddle

Mark Shust at M.academy
  • 6,300
  • 4
  • 32
  • 50
  • are you _generally_ looking for a volume of revolution? if so you need to specify the axis –  Jul 07 '15 at 14:53
  • I believe so. I'm assuming the object is completely symmetrical, so looking down on the object in a 3d world, the fish bowl is perfectly round. – Mark Shust at M.academy Jul 07 '15 at 14:58
  • 1
    Can we see the `.svg` file? – Patrick Murphy Jul 07 '15 at 14:59
  • 1
    sample the points on the edge and use the volume of revolution estimation formula –  Jul 07 '15 at 15:13
  • 2
    If you want to know the volume of a shape ask on math.stackexchange – Robert Longson Jul 07 '15 at 15:14
  • You might try some sort of reimann sum based on the svg path to get the area, and then extrapolate the volume from there assuming it's a volume of revolution. As @willywonka_dailyblah said. – xdhmoore Jul 07 '15 at 15:16
  • and oh yeah if you have complicated shapes carefully choose your step size –  Jul 07 '15 at 15:38
  • Thanks for all the feedback and comments. I updated the post with a link to jsfiddle at the bottom with a sample SVG. The full and empty levels are defined in javascript as separate points. – Mark Shust at M.academy Jul 07 '15 at 15:38
  • Thanks for the math feedback. I don't think I have any issues with that. My biggest problem is how can I figure out the points/ends of the svg element to do the math prob. Given the SVG element above, it's fairly complex, and I need to find the edges of the SVG element in order to sample it, and I'm having a tough time figuring out how to do this. – Mark Shust at M.academy Jul 07 '15 at 15:42
  • I just updated the SVG example with a much more simplified file that will be more typically used. – Mark Shust at M.academy Jul 07 '15 at 16:25

2 Answers2

2

First you need to parse the SVG path to lines. Since they all don't cross the Y axis, this reduces to finding the area under the curve caused by the fish bowl, also known as the integral.

Let {x_0, x_1, ..., x_n} be the absolute value of the X coordinates of the line segments.

The function representing the graph of the fishbowl is the piecewise function:

f(x) = 
 { (x - x_0)/(x_1 - x_0) if x_0 <= x < x_1
 { (x - x_1)/(x_2 - x_1) if x_1 <= x < x_2
 {  ... 
 { (x - x_(n-1))/(x_n - x_(n-1)) if x_(n-1) <= x < x_(n)

Then the volume of the fishbowl equals the integral of πf(x)2 (the solid of revolution formed by that function).

Let e be the empty level, v the fill level, and w the water level.

Then the ratio of the filled portion of the fishbowl is:

(∫ew πf(x)2 dx) / (∫ev πf(x)2 dx)

If instead your fishbowl is generated by the graph of a function, use that function as f(x) and then calculate the integral given above.

An integral can be approximated using numerical integration techniques such as Simpson's rule or a Newton-Cotes method.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
  • Awesome answer, just what I was looking for. Makes complete sense. Additional kudos for the math solution. Do you know of a good way of converting an SVG path to lines? I looked around at a few common ones but couldn't find anything that would work on a complex shape like https://jsfiddle.net/wc7ru8nk/ – Mark Shust at M.academy Jul 07 '15 at 18:47
  • 1
    I'm not aware of any code for parsing SVG paths to lines. Since your example uses Bézier curves, one approach is to subdivide the curve until it gets close enough to a line. Another approach is to use the Bézier cubic function as part of the piecewise curve function given in my answer. – Peter O. Jul 07 '15 at 19:02
0

A I needed a solution to this that isn't prohibitive in terms of computational cost and I wasn't in the mood to write optimized code, I ended up rendering it against transparent background, converted to raster and then counted pixels. I'm sure someone with experience in graphics and geometry can come up with cleaner solutions, but I my optimized code in a high level language is unlikely to run faster than that of someone that's dedicated their lives to this and write in assembly.

Depending on the complexity of the geometry of your fish bowl you might need to up the rendering resolution of course.

[2021 addition]

This SO answer calculates the area of one <path> using the brute-force method described above: Scaling the filling portion of an SVG path

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
KCorax
  • 317
  • 2
  • 7