Issue 101050 - avoid a corner in closed lines, which are smoothed by spline
Summary: avoid a corner in closed lines, which are smoothed by spline
Status: CLOSED FIXED
Alias: None
Product: General
Classification: Code
Component: chart (show other issues)
Version: 3.3.0 or older (OOo)
Hardware: PC All
: P3 Trivial (vote)
Target Milestone: ---
Assignee: sgautier.ooo
QA Contact: issues@graphics
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-04-13 20:08 UTC by Regina Henschel
Modified: 2013-02-24 21:18 UTC (History)
2 users (show)

See Also:
Issue Type: PATCH
Latest Confirmation in: ---
Developer Difficulty: ---


Attachments
Special handling for smoothing closed lines (1.74 KB, text/plain)
2009-04-13 20:09 UTC, Regina Henschel
no flags Details
Example, how a closed line looks now (19.88 KB, application/pdf)
2009-04-13 20:11 UTC, Regina Henschel
no flags Details
Example how it would look with the attached patch (17.22 KB, text/plain)
2009-04-13 20:12 UTC, Regina Henschel
no flags Details
invalid index access after double click on chart (19.01 KB, application/vnd.oasis.opendocument.spreadsheet)
2009-04-30 14:02 UTC, IngridvdM
no flags Details
patch to prevent invalid index access (894 bytes, text/plain)
2009-04-30 14:15 UTC, IngridvdM
no flags Details
2nd version of patch to prevent invalid index access (2.09 KB, text/plain)
2009-04-30 15:37 UTC, IngridvdM
no flags Details

Note You need to log in before you can comment on or make changes to this issue.
Description Regina Henschel 2009-04-13 20:08:14 UTC
Define a closed line by setting start and end point to the same values and
display it as xy-Chart.
Smooth the line with splines.
Notice, that you get a corner, where the start/end-point is. This corner is
produced, because the algorithm always use "natural spline" which means, that
the second derivation is zero.

The existing code has the ability to set the desired derivation for the start-
and for the endpoint. But there is no UI to do this. This issue is not about a
new UI, but to use this ability to get really "round" lines in the case, that
the line is closed.

I set the derivation to the same as the connection between second and last but
one point, which seems me to be a reasonable guess of the real, unknown derivation.

The user can force the old behavior by setting one coordinate of the start point
or of the end point to a tiny different value, for example by multiplying the
value with (1+1E-15).
Comment 1 Regina Henschel 2009-04-13 20:09:36 UTC
Created attachment 61533 [details]
Special handling for smoothing closed lines
Comment 2 Regina Henschel 2009-04-13 20:11:37 UTC
Created attachment 61534 [details]
Example, how a closed line looks now
Comment 3 Regina Henschel 2009-04-13 20:12:17 UTC
Created attachment 61535 [details]
Example how it would look with the attached patch
Comment 4 IngridvdM 2009-04-30 14:00:41 UTC
The attached patch looks fine but uncovers a different problem. I'll attach an
example to illustrate. Load the document 'Spline.ods' and double click the
chart. With the patch applied there is an invalid index access within
ViewContactOfE3dPolygon::createViewIndependentPrimitive3DSequence that leads to
a crash at least in non pro version.
Comment 5 IngridvdM 2009-04-30 14:02:40 UTC
Created attachment 61927 [details]
invalid index access after double click on chart
Comment 6 IngridvdM 2009-04-30 14:10:48 UTC
I'll create an additional patch that prevents the invalid index access within 
ViewContactOfE3dPolygon::createViewIndependentPrimitive3DSequence.
@aw, if you are ok with the changes in that method, I will commit them to one of
my next chart CWSses.
@regina, thanks for the additional off-line analysis! :-)
Comment 7 IngridvdM 2009-04-30 14:15:34 UTC
Created attachment 61928 [details]
patch to prevent invalid index access
Comment 8 Armin Le Grand 2009-04-30 14:31:22 UTC
AW: It is an error if E3dPolygonObj's GetPolyNormals3D() count or
GetPolyTexture2D() count is unequal to GetPolyPolygon3D(). This should
eventually be checked when settig data/creating that object. It needs to be a
1:1 relationship; it means that each 3D polygon point is directly associated
with a normal and a texture coordinate.
It would be better to fix that error if chart creates E3dPolygonObj's where this
1:1 relationship is broken. 'Guessing' which normal is meant to be at whcih
point as in the patch is from my point of view not a legal solution.
Comment 9 Armin Le Grand 2009-04-30 14:33:40 UTC
AW: For itself: It would be better to replace

			const bool bNormals(aPolyNormals3D.count());
			const bool bTexture(aPolyTexture2D.count());

with a more safe

			const bool bNormals(aPolyNormals3D.count() && aPolyNormals3D.count() ==
aPolyPolygon3D.count());
			const bool bTexture(aPolyTexture2D.count() && aPolyTexture2D.count() ==
aPolyPolygon3D.count());
Comment 10 Armin Le Grand 2009-04-30 14:36:59 UTC
AW: Oops.. I meant 'for safety itself'...
Comment 11 IngridvdM 2009-04-30 15:32:35 UTC
@aw, I will take your change in addition but it is not sufficient on its own. It
still crashes as in this case the main polygons are not different in size.
Instead the sub polygons are those that are different in size. They have to be
checked each in addition. But it is a good idea to introduce the same check also
for the textures. Furthermore it seems to me that some automatism ( see method
checkClosed ) remove some points from the polygons here and there thus they run
out of sync. Given that this is the main producer of this problem I think it is
a valid and good guess to take the very first point of the polygon if one is
missing in the end. I change the patch accordingly.
Comment 12 IngridvdM 2009-04-30 15:37:12 UTC
Created attachment 61930 [details]
2nd version of patch to prevent invalid index access
Comment 13 Armin Le Grand 2009-04-30 15:59:36 UTC
AW: With this version of the patch, the normals and texture coordiantes will not
be used at all, since the safe bNormals and bTexture will be false when the
count is not the same. Thus, the 2nd part of the fix is not needed anymore since
the parts using the normals and/or texture polygon will only be used when counts
are all equal.
I would suggest to take the 1st part (the changed definitions of bNormals and
bTexture), leave the 2nd part out (not needed anymore) and fix the source of the
problem: Some mechanism in chart creates E3dPolygonObj's with unequal counts of
points, normals and texture coordinates. This is not a valid thing to do.
HTH!
Comment 14 IngridvdM 2009-04-30 16:10:24 UTC
@aw, as I have explained already aPolyNormals3D.count() equals
aPolyPolygon3D.count() in this case. That is not the problem!
The problem is that aNormals3D.count() is not equal to aCandidate3D.count().
Those polygons can be created via API and they should not crash!
Comment 15 IngridvdM 2009-04-30 16:23:12 UTC
Furthermore the polygons that are set by the chart do have the proper sizes.
Points are removed by automatisms by the drawingengine code.
Comment 16 Armin Le Grand 2009-04-30 16:37:32 UTC
AW: Sorry, i missed that point. The condition for this object is simply that all
three involved polygons have to have the same internal layout, in the number of
polygons and the number of sub-polygons, too.
I am sorry that the object is constructable in the current form over the API,
but the reason for this is that it is an old object and also a special one only
used internally for chart. This is also the reason that there is not more code
in the object to ensure that conditions. Nowadays i would check and decline such
data sets.

So, the code should be something like>

			const bool bNormals(aPolyNormals3D.count() && aPolyNormals3D.count() ==
aPolyPolygon3D.count());
			const bool bTexture(aPolyTexture2D.count() && aPolyTexture2D.count() ==
aPolyPolygon3D.count());

			if(bNormals || bTexture)
			{
				for(sal_uInt32 a(0L); a < aPolyPolygon3D.count(); a++)
				{
					basegfx::B3DPolygon aCandidate3D(aPolyPolygon3D.getB3DPolygon(a));
					basegfx::B3DPolygon aNormals3D;
					basegfx::B2DPolygon aTexture2D;
					bool bNormalsSub(bNormals);
					bool bTextureSub(bTexture);
						
					if(bNormalsSub)
					{	
						aNormals3D = aPolyNormals3D.getB3DPolygon(a);
						bNormalsSub = aNormals3D.count() == aCandidate3D.count();
					}
					
					if(bTextureSub)
					{	
						aTexture2D = aPolyTexture2D.getB2DPolygon(a);
						bTextureSub = aTexture2D.count() == aCandidate3D.count();
					}

					for(sal_uInt32 b(0L); b < aCandidate3D.count(); b++)
					{
						if(bNormalsSub)
						{
							aCandidate3D.setNormal(b, aNormals3D.getB3DPoint(b));
						}
						
						if(bTextureSub)
						{
							aCandidate3D.setTextureCoordinate(b, aTexture2D.getB2DPoint(b));
						}
					}

					aPolyPolygon3D.setB3DPolygon(a, aCandidate3D);
				}
			}

Still, the error is in the data provider in the chart. The whole design of
E3dPolygonObj should make clear that it|s purpose was to have exactly the same
layout for all polygons and that the polygons for texture and normals are only
helper polygons for data transport. Today, a basegfx::B3DPolyPolygon which is
capable of holding all that data would be used. Since it is not, it IS necessary
to construct one from the old data distribution in three single polygons.

You may write me an issue to update the implementation of E3dPolygonObj to
fulfill all modern needs. I just can't say when i will be able to do this. We
should also update the API documentation (if this internal object is documented
at all) that normal and texture coordiante PolyPolygons will only be accepted
when their layout is equal to the base poygon (the one with the points).
Comment 17 Armin Le Grand 2009-04-30 16:43:42 UTC
AW:
> Points are removed by automatisms by the drawingengine code

?!? Please describe this closer. This should not happen since the API should
internally use the new basegfx polygons. If they still use the tools Polygons,
this may lead to a conversion error; When 1st and last points are equal in the
old polygon, it was a needed compromize to migrate to the new polygons (the old
ones had no closed state, but equal 1st and last points). To force the same for
all polygons, it should work to use the same conventions e.g. for the normal
polygon (1st and last normal the same, when points do this).
Comment 18 IngridvdM 2009-04-30 17:01:32 UTC
This is how points are removed by automatisms by the drawingengine code:
The normals polygon is set via the API with correct sizes. This leads to the call
Svx3DPolygonObject::setPropertyValueImpl - case
OWN_ATTR_3D_VALUE_NORMALSPOLYGON3D. Here the method
PolyPolygonShape3D_to_B3dPolyPolygon is called which itself calls
basegfx::tools::checkClosed(aNewPolygon);
-> polygons are out of sync.
Comment 19 Armin Le Grand 2009-04-30 17:29:38 UTC
AW: Loking at svx/source/unodraw/unoshape.cxx: All three cases
OWN_ATTR_3D_VALUE_POLYPOLYGON3D, OWN_ATTR_3D_VALUE_NORMALSPOLYGON3D and
OWN_ATTR_3D_VALUE_TEXTUREPOLYGON3D have identical implementations. If the
results are different, the source must have been different.

Are You sure the normals polygon mimics the point polygon in having an extra end
point equal to the start point when You are using a closed point polygon? Please
re-check.
Comment 20 IngridvdM 2009-04-30 17:43:59 UTC
I set the same amount of points to all polygon properties. Several normals
within the normals polygon seem to point into the same direction thus the
automatism removes them.
Comment 21 IngridvdM 2009-04-30 17:50:52 UTC
Function checkClosed(B3DPolygon& rCandidate) does remove an 'arbitrary' amount
of points depending on the concrete values of the points in the polygons.
Comment 22 Armin Le Grand 2009-04-30 18:08:20 UTC
AW: Ah, Yes! Da**ed! Normals are simply not points. Argh! As can be seen, a
compromize of older ages (transport normals as polygon, there was tooling for
this already) leads to growing problems in the future :-(

I have no quick idea how to fix this, but the error definitely happens in the
API implementation (Svx3DPolygonObject::setPropertyValueImpl). For
OWN_ATTR_3D_VALUE_NORMALSPOLYGON3D and for OWN_ATTR_3D_VALUE_TEXTUREPOLYGON3D
PolyPolygonShape3D_to_B3dPolyPolygon should not call
basegfx::tools::checkClosed; a flag at PolyPolygonShape3D_to_B3dPolyPolygon may
do that. In that case, the normal and/or texture polygon can never have less,
but maybe one more 'point' than the original (when the original was closed). The
safety decisions proposed in
ViewContactOfE3dPolygon::createViewIndependentPrimitive3DSequence would have to
be adapted to not only work on normal and/or texture data with the same, but
with the same or more (>= instead of ==) points.

We need to write a task for cleaning this up in the future. The API is simply
not good designed for the purpose here...
Comment 23 Armin Le Grand 2009-04-30 18:15:23 UTC
AW: Another idea: When there is a normals polygon, but less normals than points,
it's because double points were removed -> imply all non-existent normals to be
equal to the 1st normal (since double points are removed from the end of a polygon).
Congratulations IHA, this is exactly the fix You already proposed! You get all
my kudos :-) At least we know the reason now :-(
When we take Your fix (it's easier and better, i think), we should definitely
file a task to clean this up in the future ...
Comment 24 IngridvdM 2009-05-04 09:22:22 UTC
Thanks Armin! :-) I created Issue 101520 for further cleanup of the API
implementation. And will commit patch version 2 to CWS chart39. Thanks again for
the quick review and further analysis!
Comment 25 IngridvdM 2009-05-04 17:10:17 UTC
Fixed in CWS chart39.
Comment 26 IngridvdM 2009-07-01 12:15:24 UTC
@Sophie, please verify in CWS chart39. Thanks, Ingrid!
Load the attached document Spline.ods and double click the chart. Select 'Chart
Type...' in the contextmenu and click on the 'Lines only' Image to switch to 2D.
Compare the result with the attached pdf. The blue line should more look like
new_way.pdf and not like old_way.pdf.
Comment 27 sgautier.ooo 2009-07-02 11:29:06 UTC
Verified in CWS chart39 - The blue line looks like in new_way.pdf. Sophie
Comment 28 sgautier.ooo 2009-08-05 14:07:55 UTC
Verified in DEV300m54 under .deb version - Closing - Sophie