sketchucation logo sketchucation
    • Login
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info

    Can't draw subclasses of Sketchup::Edge

    Scheduled Pinned Locked Moved Developers' Forum
    25 Posts 4 Posters 672 Views 4 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • G Offline
      Grays42
      last edited by

      thomthom, I appreciate that, but I'm already there--I'm doing that. I'm trying to explain my issue in detail, and I'm miscommunicating.

      My difficulty is that the entity is no longer extended with the mixin when the entity is passed into onEraseEntity by an observer, and using .extend(Gcode) again inside onEraseEntity adds the methods, but not any of the data.

      coupled_edge is a mixin class method that functions correctly until the user deletes the edge. The deletion triggers the observer that I've attached to the edge, then it's supposed to execute this code. Note that all of the involved edges in this example have been extended as Gcode; the entity and the two adjoining edges.

      module Gcode
         class GcodeEdgeObserver < Sketchup;;EntityObserver
            def onEraseEntity(entity)
               entity.vertices.each do |v|
                  adjoining_edge = entity.coupled_edge(v) #throws undefined method
                  adjoining_edge.decouple_at_vertex(v)
                  #other cleanup methods here
               end
            end
         end
      end
      

      At this point, the entity passed into onEraseEntity no longer contains any mixin methods, and is just a core class with no other functions. I have tried a number of calls to the mixin methods and variables on the entity that is passed into onEraseEntity and none of them work. It is simply no longer extended at that point. Calling .extend(Gcode) on the entity within onEraseEntity gives it Gcode methods again, but none of the data.

      That's why I posted about making a subclass in the first place; everything in this thread has been an attempt to work around the onEraseEntity limitations. As it stands, I have absolutely no way to run a cleanup routine, as the necessary functions don't exist in the scope of onEraseEntity. Any suggestions for how to work around it?

      1 Reply Last reply Reply Quote 0
      • thomthomT Offline
        thomthom
        last edited by

        @grays42 said:

        And I'm only extending the base class within the namespace of my module, as we previously discussed. Does that cause conflicts? If so, what is your recommendation for adding the functionality I'm after to the base class without conflicting with other plugins?

        Yes, any module you extend will be there for everyone else.

        If you have defined your mixin module Bar within your namespace Foo adding method #hello then any other script that iterate the entities will have access to method #hello and the entity will return true to entity.is_a?(Foo::Bar). The effect of using #extend is global. Hence the chance of conflicts.

        Thomas Thomassen β€” SketchUp Monkey & Coding addict
        List of my plugins and link to the CookieWare fund

        1 Reply Last reply Reply Quote 0
        • G Offline
          Grays42
          last edited by

          Ok, I understand now.

          So, as a solution, what if I put an extremely unique prefix in every public function definition in the base class extension? Would that be sufficient, or is there a more elegant way to get the functionality I want?

          1 Reply Last reply Reply Quote 0
          • G Offline
            Grays42
            last edited by

            The code (very rough draft):

            module Gcode
            	class Sketchup;;Edge
            		def self.entity_id_is_gcode?(entity_id)
            			return @@gcode_edges.keys.include?(entity_id)
            		end
            		
            		def self.get_gcode_edge_by_id(entity_id)
            			return @@gcode_edges[entity_id]
            		end
            	end
            
            	module GcodeEdge
            		class Sketchup;;Edge
            			@@gcode_edges = {}
            			
            			def extend(base)
            				@is_gcode = true
            				@@gcode_edges[self.entityID] = self
            			end
            			
            			def is_gcode?
            				return false if @is_gcode.nil? or @is_gcode == false
            				return true
            			end
            			
            			def asdf
            				puts "Success! Successfully retrieved instance data while in onEraseEntity." if self.is_gcode?
            			end
            		end
            	end
            end
            

            The observer:

            module Gcode
            	class GcodeEdgeObserver < Sketchup;;EntityObserver
            		def onEraseEntity(entity)
            			puts entity.entityID #output is always the negative of the ID
            			
            			#Attempting to call on the entity actually passed to onEraseEntity
            			puts "Attempting to puts based on a class variable of the erased entity (should NOT work);"
            			entity.asdf
            			
            			#The REAL entity;
            			puts "Attempting to puts based on an instance variable of the erased entity (SHOULD work);"
            			pre_deleted_entity = Sketchup;;Edge.get_gcode_edge_by_id(-1*entity.entityID)
            			pre_deleted_entity.asdf
            
            	        end
            	end
            end
            

            The relevant lines from my unit tests (deleted anything not related):

            module Gcode
            		def self.unit_test_gcode_vertex
            			puts "-----------TESTING GcodeVertex-----------"
            			
            			p1 = Geom;;Point3d.new(0,0,0)
            			p2 = Geom;;Point3d.new(0,1,0)
            			
            			e1 = Sketchup.active_model.entities.add_edges(p1,p2)[0]
            			e1.add_observer(GcodeEdgeObserver.new)
            			puts e1.entityID
            			
            			e1.extend(GcodeEdge)
            			e1.erase!
            			puts "-----------------------------------------"
            		end
            end
            

            Calling unit_test_gcode over and over produces this output (notice the IDs!):

            -----------TESTING GcodeVertex-----------
            2069
            -2069
            Attempting to puts based on a class variable of the erased entity (should NOT work);
            Attempting to puts based on an instance variable of the erased entity (SHOULD work);
            Success! Successfully retrieved instance data while in onEraseEntity.
            -----------------------------------------
            -----------TESTING GcodeVertex-----------
            2074
            -2074
            Attempting to puts based on a class variable of the erased entity (should NOT work);
            Attempting to puts based on an instance variable of the erased entity (SHOULD work);
            Success! Successfully retrieved instance data while in onEraseEntity.
            -----------------------------------------
            -----------TESTING GcodeVertex-----------
            2079
            -2079
            Attempting to puts based on a class variable of the erased entity (should NOT work);
            Attempting to puts based on an instance variable of the erased entity (SHOULD work);
            Success! Successfully retrieved instance data while in onEraseEntity.
            -----------------------------------------
            -----------TESTING GcodeVertex-----------
            2084
            -2084
            Attempting to puts based on a class variable of the erased entity (should NOT work);
            Attempting to puts based on an instance variable of the erased entity (SHOULD work);
            Success! Successfully retrieved instance data while in onEraseEntity.
            -----------------------------------------
            -----------TESTING GcodeVertex-----------
            2089
            -2089
            Attempting to puts based on a class variable of the erased entity (should NOT work);
            Attempting to puts based on an instance variable of the erased entity (SHOULD work);
            Success! Successfully retrieved instance data while in onEraseEntity.
            -----------------------------------------
            

            So...it works! As far as I can tell, this will be a 100% consistent and reliable workaround for the fact that the wrong entity is passed into onEraseEntity.

            1 Reply Last reply Reply Quote 0
            • G Offline
              Grays42
              last edited by

              I figured out a workaround! I accessed the correct entity in onEraseEntity!

              During testing, it turned out that the entityID of the entity passed into onEraseEntity in the observer is always the negative of the entityID of the erased entity, AND the real entity still exists and can execute mixin instance methods and retrieve instance data.

              In other words, if real_entity.entityID == 1923, and has an observer, then once you call real_entity.erase!, the observer calls onEntityErase(wrong_entity). I discovered through testing that, inside onEntityErase, wrong_entity.entityID == -1923.

              So...all I did was create global_hash_of_real_entities = {}, keyed by real_entity.entityID every time a real_entity is initialized, with the value being a reference to real_entity. Inside onEntityErase, you can access the pre-deleted real_entity by evaluating global_hash_of_real_entities[-1*wrong_entity.entityID].

              The only caveat is that you can't use any base methods in real_entity, they'll throw errors. However, if there's some functionality you need during the cleanup routine, you can duplicate it with a mixin method and that method will still exist during onEraseEntity.

              I got a test to produced this output:

              -----------TESTING GcodeVertex-----------
              Attempting to puts based on an instance variable of the erased entity (should NOT work);
              Attempting to puts based on an instance variable of the erased entity (SHOULD work);
              Success! Successfully retrieved instance data while in onEraseEntity.
              -----------------------------------------
              

              The second example produced a string that would only successfully trigger if the workaround entity (inside onEraseEntity) had a boolean switched to true BY an instance method in the entity passed into onEraseEntity prior to being erased. The solution does involve a class variable hash, but given a unique enough name, it shouldn't cause a problem.

              1 Reply Last reply Reply Quote 0
              • thomthomT Offline
                thomthom
                last edited by

                That's a mighty interesting find! I'll try and play with that myself.

                I'm still concerned about extending base class. It's so many ways there can be unforeseen trouble. I'd have to say it'd have to be last resort and the names needs to be very unique. (Remember that the API could add new methods in new releases.)

                If SketchUp get around to use Ruby 2.0 there might be a better way around this as there appear to be a new feature in Ruby where one can make local modifications.

                Btw, instead of -1*entity.entityID you can just use entity.entityID.abs.

                ... I have to say - I don't quite understand how your extend module works... πŸ˜• I thought one passed a module to #extend() - and the module methods could be added to the instance you extended. But you have a class within your GcodeEdge module.... ?

                Thomas Thomassen β€” SketchUp Monkey & Coding addict
                List of my plugins and link to the CookieWare fund

                1 Reply Last reply Reply Quote 0
                • G Offline
                  Grays42
                  last edited by

                  @thomthom said:

                  That's a mighty interesting find! I'll try and play with that myself.

                  Thanks! Just remember: the real entity's base instance methods do not work even if you've retrieved the correct entity, only the mixin methods and data work. Calling something as simple as entityID on the real entity within onEraseEntity will throw an error, but if I've mixed in module functionality, all of the module's functions and variables work fine.

                  That's given me enough to work with that I have completely solved my cleanup problem.

                  @thomthom said:

                  I'm still concerned about extending base class. It's so many ways there can be unforeseen trouble. I'd have to say it'd have to be last resort and the names needs to be very unique. (Remember that the API could add new methods in new releases.)

                  ... I have to say - I don't quite understand how your extend module works... πŸ˜• I thought one passed a module to #extend() - and the module methods could be added to the instance you extended. But you have a class within your GcodeEdge module.... ?

                  I'm still fumbling around with module as it is, my priority right now is to get everything to work. I'm building a pretty robust unit testing suite as I go and I test every other line or so, so if I mess up the module and break something, I'll know it. The entire point of this is for personal use, I'm not focusing on release...but I don't mind future-proofing it either, if there's a way to get what I want done.

                  By now we've discussed it enough that I believe you have a pretty good idea of what I'm trying to do...if you have a suggestion for a different way I should implement this so it plays nicely, I'm open to suggestions.

                  1 Reply Last reply Reply Quote 0
                  • thomthomT Offline
                    thomthom
                    last edited by

                    Well, since you can't call the native methods on the erased entities you could make your identity hash refer to a data container with your data instead of the entity itself. And then avoid extending the Sketchup::Edge entities.

                    But if it's entirely for internal use then you're a lot safer as then you control the environment. Trouble starts when one release it for consumers.

                    Thomas Thomassen β€” SketchUp Monkey & Coding addict
                    List of my plugins and link to the CookieWare fund

                    1 Reply Last reply Reply Quote 0
                    • G Offline
                      Grays42
                      last edited by

                      Oh, hell, I'm an idiot. I understand what you mean now, and I'm not sure why it took me this long.

                      Okay, I've rebuilt almost all of my functionality into its own class (class GcodeEdge in module Grays42_Gcode_Paths) that stores a single reference to the associated edge as an instance variable. All of the actual toolpath building is done by a container class, GcodeRoute, that stores a stack of GcodeEdges and never actually touches the edge entities themselves. Everthing works so far.

                      That should resolve any potential conflicts. Sorry about that.

                      1 Reply Last reply Reply Quote 0
                      • thomthomT Offline
                        thomthom
                        last edited by

                        πŸ‘ πŸ‘ πŸ˜„

                        I hope we get to see your creation one day. It sounds interesting!

                        Thomas Thomassen β€” SketchUp Monkey & Coding addict
                        List of my plugins and link to the CookieWare fund

                        1 Reply Last reply Reply Quote 0
                        • 1
                        • 2
                        • 2 / 2
                        • First post
                          Last post
                        Buy SketchPlus
                        Buy SUbD
                        Buy WrapR
                        Buy eBook
                        Buy Modelur
                        Buy Vertex Tools
                        Buy SketchCuisine
                        Buy FormFonts

                        Advertisement