I have exactly the same use case at work, so this is what I have come up with:
Solution 1 (ellipse)
This looks fairly regular, but is the least precise generally, IMO.
Use the ellipse marker symbology with the following data-defined overrides and change the units from Millimeter to Map Unit (essential!):
Symbol width: "E" + "W"
Symbol height: "N" + "S"
Offset X,Y: concat(("E"+"W")/2 - "W",',',("N"+"S")/2 - "N")
You will then get an ellipse that should roughly approximate your crown spread relative to your tree location. The height and width will obviously correspond to your crown measurements, but actual crown spread at cardinal points may be slightly off (see comparison of T108 below).
With rather uneven crowns, it's definitely more representative than Solution 3 (compare T080 below), but outside of these exceptions I find it tends to overestimate the most.
If you have any crown measurements of 0 and the ellipse is way off, setting Offset X,Y to concat(("E"+"W")/2 - clamp(1,"W",100),',',("N"+"S")/2 - clamp(1,"N",100))
has made it less jarring in most cases..
Geometry generator: The rest of the solutions use the Geometry Generator styling available from 2.14 onwards - as per the image below, (1) select the geometry generator styling, (2) select Polygon/Multipolygon geometry type and then (3) enter the expression provided. Check out Anita Graser's blog for more info.

Solution 2 (8-vertex polygon)
This looks the ugliest, but is also the most precise, as the polygon will always hew to the cardinal crown spread measurements.
Refer to method described here (in relation to another question I posted).
Solution 3 (convex hull)
However, revisiting the 4-circles answer above led me to a solution that is most visually attractive, but will overestimate wildly uneven crown spreads (see below).
Use the geometry generator symbology and generate a polygon per point with the following expression:
convex_hull(combine(buffer(make_point($x-("W"/2),$y),"W"/2),
combine(buffer(make_point($x+("E"/2),$y),"E"/2),
combine(buffer(make_point($x,$y+("N"/2)),"N"/2),
buffer(make_point($x,$y-("S"/2)),"S"/2)))))
This generates 4 circles where the outermost point touches the cardinal crown spread points, combines the circles, then generates a convex hull around the combined circles (i.e. smallest geometry that contains all outer nodes of a set of polygons)
You will get a crown spread like below (note how it wraps around the 4 circles, and compare it with the 8-vertex polygon, and the ellipse)

For the most part, the convex hull will not go beyond the cardinal crown spread measurements, but if you have a much shorter measurement in between two longer ones, it will go right out, unlike the ellipse. Refer to the following image - T080 has a canopy spread of 8/12/4/12 (N/E/S/W, metres).

Solution 4 (smoothed 8-vertex polygon) (QGIS 3)
With the introduction of the smooth()
function in QGIS 3 you can now get more accurate canopy spreads that look prettier:

Wrap smooth() around the 8-vertex polygon (Solution 2) like so:
case when "n" = "e" and "e" = "s" and "s" = "w" and "w" = "n"
then buffer($geometry,"N") else
smooth(make_polygon(make_line(
translate($geometry,0,max("N",0.2)),
translate($geometry,0.6*max("E",0.2),0.6*max("N",0.2)),
translate($geometry,max("E",0.2),0),
translate($geometry,0.6*max("E",0.2),-0.6*max("S",0.2)),
translate($geometry,0,-max("S",0.2)),
translate($geometry,-0.6*max("W",0.2),-0.6*max("S",0.2)),
translate($geometry,-max("W",0.2),0),
translate($geometry,-0.6*max("W",0.2),0.6*max("N",0.2)),
translate($geometry,0,max("N",0.2)))),
10,0.1,0.1,70) end
I recommend keeping the 'min_line' value to 0.1 (metres), while playing around with the 'offset' and 'angle' values (here set to 0.1 and 70 respectively).
Drawbacks:
- The symbols won't render properly in legends. You may use a custom symbol (QGIS 3.14 onwards) to manually represent the legend item.
- You obviously can't use the symbols to perform spatial analyses as these are styles, not actual geometries. To get around this you can create a polygon layer using the above expression in the 'Geometry by expression' processing tool, but this will be a static layer and will not reflect any changes from your source data. If you require dynamically generated polygon geometries, look into Virtual Layer (sample SQL query in this answer)