What defines an identity transformation?
-
Its an extremely common optimization to mark transforms that have never been assigned to skip processing. Rather than fiddle around deciding whether a transform "has an effect" based on some epsilon.
So the method is correct but not complete.
-
But surely, the flag could be set in the
#new
method in all of these cases?The flag doesn't have to be computed on every call, just when the matrix' values has been changed - and that's set in
#new
and#set!
. Right? -
And it's certainly something that would be helpful if the documentation mentioned.
-
@thomthom said:
The flag doesn't have to be computed on every call, just when the matrix' values has been changed - and that's set in
#new
and#set!
. Right?ALSO ...
t3 = Geom::Transformation.axes( Geom::Point3d.new(0, 0, 0), Geom::Vector3d.new(1,0,0), Geom::Vector3d.new(0,1,0), Geom::Vector3d.new(0,0,1)) %(#008000)[>> #<Geom::Transformation:0x753fe38>] t3.identity? %(#008000)[>> false]
-
ALSO ...
t4 = Geom::Transformation.scaling( 1.0 ) %(#008000)[>> #<Geom::Transformation:0x7621cc0>] t4.identity? %(#008000)[>> false] t4.to_a %(#008000)[>> [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]]
-
I would consider this an API bug, myself.
Workaround 1:
def indentity_transform?(t) t.to_a == [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] end
Workaround 2, a mixin module:
module Mixin; end module Mixin;;Identity def indentity?() self.to_a == [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] end end # Mixin module
Use mixin to change only individual transforms thus:
require("Mixin/indentity.rb") # .. later tran = Geom;;Transformation.scaling( 1.0 ) tran.extend(Mixin;;Idenitity) if tran.identity? # do something else # do something else end
-
If you want the optimisation you do the check in #new and #set!
-
But if you apply an Identity, it is, by definition, a transform that changes nothing, so no harm done.
The method may not cover all cases, but its not really wrong either.
The problem, Dan & Thomthom, is floating point precision issues. You can't just compare to an array of 1,0,0,..., because, as you well know, comparing floating point numbers for equality is "A Bad Thing".
And sure, stick the test in #Set, and probably triple the execution time of that method and assume its not called often. And keep in mind its not actually catching all cases because of float precision...
So then you have to look to generate the full orthonormal transform and check it's unit length etc..
We've all been here, walk away. Really.
-
@adamb said:
But if you apply an Identity, it is, by definition, a transform that changes nothing, so no harm done.
Except testing the transformation to be an identity transformation is faster than iterating over thousands of points applying the transformation.
And for the usable I was looking for this in my current project the values fed to the transformation object would be true 0 - so floating point precision would not be an issue in this case.
But I guess it's just as well to make special case for that in my code instead of the transformation code. Though still wish the API would describe the actual behaviour of #identity?
-
@adamb said:
The problem, Dan & Thomthom, is floating point precision issues. You can't just compare to an array of 1,0,0,..., because, as you well know, comparing floating point numbers for equality is "A Bad Thing".
(Headsmack!)
Right.
Well then the API's internal tolerance must be used, so for example compares like:
t_identity.xaxis == t_other.xaxis t_identity.xscale.to_l == t_other.xscale.to_l
But yes, very slow, new vector and point objects need to be created on both sides of the operator, for all x, y, z, etc. (You might save a bit of time, by using the global objects
X_AXIS
,Y_AXIS
, etc.)Not to mention the overhead of calling all those methods.
The API just needs updating / expanding on the C-side of things.
-
I guess the docs could be updated to be:
returns true if it is Identity
returns false if its undetermined (ie, it might be)@thomthom said:
Except testing the transformation to be an identity transformation is faster than iterating over thousands of points applying the transformation.
Sure, if that is your bottleneck. Which it probably isn't.
Adam
-
It's part of it. For when the tool is being used with live preview and thousands of points are being constantly computed - every set of iteration eats some time.
The absolute biggest is how slow SketchUp is to add geometry. But that's not a whole lot I can do anything about.
-
FYI: I DID notice that when you use
clone()
on an identity transform, that the new one correctly has the identity flag settrue
. -
How about this instead for simplicity, and to test using SketchUp's internal tolerance?
module WhatEver ZERO = 0.0.to_l UNIT = 1.0.to_l IDENT = [ UNIT, ZERO, ZERO, ZERO, ZERO, UNIT, ZERO, ZERO, ZERO, ZERO, UNIT, ZERO, ZERO, ZERO, ZERO, UNIT ] def indentity_transform?(t) # t.to_a.map{|m|m.to_l} == IDENT # end end # module WhatEver
-
Which also leads to the conclusion that the
Geom::Transformation
class needs a properly overriden comparison methods<=>()
,==()
, andeql?()
...
Advertisement