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

    How to cut from object

    Scheduled Pinned Locked Moved Developers' Forum
    47 Posts 4 Posters 3.9k 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.
    • TIGT Offline
      TIG Moderator
      last edited by

      In both SketchUp versions, all scripts automatically load from the Plugins folder as SketchUp starts.
      If their code includes some 'self-running' lines, then those will start a process you desire...
      For example, this happens with Observers etc - like my LayerWatcher toolset, which adds an observer to the model's layers, and also some extra functions to the context-menu etc...

      Also once some process has completed then you can use various API methods to save the model as a SKP [even with different version] or other exportable formats [more are available in Pro], you do not have to prompt for a file-name, although it is an option, your code can [re]set the file name so the original is not overwritten, even make a new sub-folder to take the file[s] etc...

      TIG

      1 Reply Last reply Reply Quote 0
      • A Offline
        ado130
        last edited by

        Now I've it in Plugins->Extensions, so then I only "delete" this manually run script and it should work automatically.
        I suppose that this API can be used as a script.
        I only need save it πŸ˜„ another work will do a C# program, then it's easy.

        About notches, can you help with that? I'm a bit lost how to proceed now.

        Two more questions:

        1. is possible do a fillet (radius) on rectangel blocks?
        2. Exist a way, how to secure script, I think sth like non-readable, *.rb is possible open in whatever and *.rbz is just a "zip", so..
        1 Reply Last reply Reply Quote 0
        • TIGT Offline
          TIG Moderator
          last edited by

          @ado130 said:

          Now I've it in Plugins->Extensions, so then I only "delete" this manually run script and it should work automatically.
          I suppose that this API can be used as a script.
          I only need save it πŸ˜„ another work will do a C# program, then it's easy.

          About notches, can you help with that? I'm a bit lost how to proceed now.

          Two more questions:

          1. is possible do a fillet (radius) on rectangle blocks?
          2. Exist a way, how to secure script, I think sth like non-readable, *.rb is possible open in whatever and *.rbz is just a "zip", so..

          To run it manually leave the menu adding code in, put # in front of each line later to stop that menu forming...

          I don't understand your latest 'question' about notches...

          1. You can do a 3d fillet on a 3d block -see Fredo's RoundCorner tool for a tour-de-force on that.
            But in principle you and a 1/4 circle 'negative-out-of-a-square face' perpendicular onto the center of selected path edge and use those path edges with a followme command to subtract the geometry...

          2. The standard way to make a script is to make am RB loader file and a same-name subfolder containing your main code.
            The RB sets up the Extension and loads the code file with in the subfolder by name [no file-type needed] [that code can then 'require' other files in there too].
            The RB and subfolder of files are supplied in an RBZ archive - this is simply a ZIP file renamed with another file-type. Preferences > Extensions > Install Extension processes the RBZ and adds the RB+subfolder into the user's Plugins subfolder.
            RBZ is not secure as it's a binary format ZIP file which can easily be extracted.
            When developing scripts you'd normally leave all of your scripts in the subfolder as RB for ease of access.editing etc.
            If you want to stop others seeing your main code you can encrypt the RB files within the RBZ's subfolder - the loader must remain an RB file - but it won't [shouldn't] contain sensitive code anyway...
            The most common encryption seen is RBS. This works on all SketchUp versions but is known to have be cracked long ago, and so it's relatively weak - although it stops the casual user from seeing the contents. There used to be a standalone exe available from SketchUp to do this encryption, since v2016's launch that's been withdrawn.
            Now you must register as a developer and submit to SketchUp your full RBZ containing RB files and they will process it and you can download it to distribute [ http://extensions.sketchup.com/en/developer_center/extension_security ].
            The returned RBZ contains a 'signed' hash file which seeks to ensure >=v2016 users, albeit weakly, that the contents of that RBZ have not be changed at all since that 'signing' process [i.e. the original RB/RBS/RBE/JS/CSS/HTM/HTML].
            When submitting the RBZ you can choose to leave the subfolder's scripts as RB files, or encrypt them.
            The two methods offered are RBS and RBE.
            As said earlier, the RBS encryption is relatively weak, but it has the advantage of being usable on all current SketchUp versions [although the signing site says it's for v2015 and older it will also work in v2016.
            The newer RBE format has not yet been cracked [as far as we know], so it offers more security.
            However, you will then limit your potential user-base to those with >=v2016 - something to consider for a commercial extension, where the very nature of encryption is paramount, but numbers count too.
            So if you want protection choose either RBS or RBE, as you consider appropriate.
            Depending on which you variant choose, all RB files in subfolder the RBZ are processed and replaced with encrypted versions.
            Oddly the signing site lets you choose to have both RBS and RBE encrypted versions in the same RBZ.
            IMHO this is somewhat reckless - the hackers out to get your intellectual property can readily crack the RBS version anyway, and even worse - since the RBS and RBE contain identical code when decrypted it gives the hackers a told-hold in breaking the RBE encryption set up - since they then know what should be produced during decryption... So if you decide that RBE is for you, then never supply an RBS version with it too...
            If you compile some of your code in C+ etc that's binary and hard to decrypt.
            If you want to sell it as licensed software there are methods available via the API and SketchUp's EW, or several alternative private licensing methods too...

          TIG

          1 Reply Last reply Reply Quote 0
          • A Offline
            ado130
            last edited by

            Thanks for comprehensive answer, I'll try it.
            In first point you describe, when the rectangular exist, is it good way?, or is better to do fillet while drawing?

            About notches

            		active_edges = model.active_entities.grep(Sketchup;;Edge)					
            		edges = Sketchup.active_model.active_entities.add_circle(center, dir, radius)
            		new_edges = model.active_entities.grep(Sketchup;;Edge) - active_edges
            		togos = [] ### empty array
            		new_edges.each{|edge| togos << edge unless edge.faces[0] }
            		model.active_entities.erase_entities(togos) if togos[0]
            		fac = nil
            		new_edges[0].faces.each{|f|fac = f unless f.loops[1]}
            		fac.pushpull(-depth)
            

            It's not a whole code, but the rest is the same (see above).
            But still is there a problem.

            1 Reply Last reply Reply Quote 0
            • TIGT Offline
              TIG Moderator
              last edited by

              Do you run it with the Ruby Console open ?
              What are the error messages ?

              To see output at each stage add p in front of the operation...
              e.g.

              p active_edges = model.active_entities.grep(Sketchup::Edge) p edges = Sketchup.active_model.active_entities.add_circle(center, dir, radius) p new_edges = model.active_entities.grep(Sketchup::Edge) - active_edges ... p togos ... p fac

              To show how each has been set...

              TIG

              1 Reply Last reply Reply Quote 0
              • A Offline
                ado130
                last edited by

                http://i.imgur.com/3ihSgfl.png

                237: new_edges[0].faces.each{|f|fac = f unless f.loops[1]}

                1 Reply Last reply Reply Quote 0
                • sdmitchS Offline
                  sdmitch
                  last edited by

                  @ado130 said:

                  		active_edges = model.active_entities.grep(Sketchup;;Edge)					
                  > 		edges = Sketchup.active_model.active_entities.add_circle(center, dir, radius)
                  > 		new_edges = model.active_entities.grep(Sketchup;;Edge) - active_edges
                  > 		togos = [] ### empty array
                  > 		new_edges.each{|edge| togos << edge unless edge.faces[0] }
                  > 		model.active_entities.erase_entities(togos) if togos[0]
                  > 		fac = nil
                  > 		new_edges[0].faces.each{|f|fac = f unless f.loops[1]}
                  > 		fac.pushpull(-depth)
                  

                  But still is there a problem.

                  Problem

                  1. .add_circle doesn't create a face only the bounding edges. new_edges=edges1. the circle is being added to the model context therefore there is no interaction with the entities in the group. add a group inside the original group. save the faces in original group. do the .add_circle inside the new group then explode it. this will cause the circle edges to merge with the original group and create the circle face. circle_face=current_faces-faces. delete new_edges that have no associated face.

                  Nothing is worthless, it can always be used as a bad example.

                  http://sdmitch.blogspot.com/

                  1 Reply Last reply Reply Quote 0
                  • TIGT Offline
                    TIG Moderator
                    last edited by

                    Thanks Sam.
                    Somewhere in the many iterations of this code... the circle in the group and its explosion got lost...
                    After that it should work πŸ˜‰

                    TIG

                    1 Reply Last reply Reply Quote 0
                    • A Offline
                      ado130
                      last edited by

                      I am slightly confused now πŸ˜„ because before you said

                      @unknownuser said:

                      You don't need to add the edges to a new group.
                      raw it directly over the face in the active_entities.
                      edges = Sketchup.active_model.active_entities.add_circle(center, dir, radius, segs)

                      So what should I do?
                      If I understood Sam correctly, I need sth like
                      group = Sketchup.active_model.active_entities.add_group() gents = group.entities active_faces = model.active_entities.grep(Sketchup::Face) edges = gents.add_circle(center, dir, radius) group.explode circle_face = model.active_entities.grep(Sketchup::Face) - active_faces
                      .. and then?
                      I'm not sure what is meant by "add a group inside the original group".

                      By the way, thanks Sam, or, thanks to both.

                      1 Reply Last reply Reply Quote 0
                      • sdmitchS Offline
                        sdmitch
                        last edited by

                        @ado130 said:

                        group = Sketchup.active_model.active_entities.add_group() gents = group.entities active_faces = model.active_entities.grep(Sketchup::Face) edges = gents.add_circle(center, dir, radius) group.explode circle_face = model.active_entities.grep(Sketchup::Face) - active_faces
                        .. and then?
                        I'm not sure what is meant by "add a group inside the original group".

                        By the way, thanks Sam, or, thanks to both.

                        ###group = Sketchup.active_model.active_entities.add_group();
                        ### remove previous statement since you have already defined "group".
                        gents = group.entities
                        active_faces = gents.grep(Sketchup;;Face);### modified
                        circle_group = gents.add_group; cge = circle_group.entities;### new
                        edges = cge.add_circle(center, dir, radius);### modified
                        circle_group.explode;### new
                        circle_face = (gents.grep(Sketchup;;Face) - active_faces)[0];### modified
                        
                        

                        Nothing is worthless, it can always be used as a bad example.

                        http://sdmitch.blogspot.com/

                        1 Reply Last reply Reply Quote 0
                        • A Offline
                          ado130
                          last edited by

                          I had never defined the group in this method previous.
                          This is what I have now:

                          def notch(x, y, z, radiuses, depths, directions)
                          	model = Sketchup.active_model 
                          	countOfNotch = x.length.to_i
                          	countOfNotch.times do |i|
                          		
                          		center = [x[i], y[i], z[i]].map(&;to_f)		
                          		radius = radiuses[i]
                          		depth = depths[i]
                          		direction = directions[i]
                          		
                          		case direction
                          			when "N"
                          				dir = Geom;;Vector3d.new(0,0,1)
                          			when "E"
                          				dir = Geom;;Vector3d.new(-1,0,0)
                          			when "S"
                          				dir = Geom;;Vector3d.new(0,0,-1)
                          			when "W"
                          				dir = Geom;;Vector3d.new(1,0,0)		
                          			when "F"
                          				dir = Geom;;Vector3d.new(0,-1,0)	
                          			when "B"
                          				dir = Geom;;Vector3d.new(0,1,0)				
                          			else
                          				dir = Geom;;Vector3d.new(0,0,1)		
                          		end		
                          		
                          		gents = group.entities
                          		active_faces = gents.grep(Sketchup;;Face);### modified
                          		circle_group = gents.add_group; cge = circle_group.entities;### new
                          		edges = cge.add_circle(center, dir, radius);### modified
                          		circle_group.explode;### new
                          		circle_face = (gents.grep(Sketchup;;Face) - active_faces)[0];### modified	
                          		circle_face.pushpull(-depth)		
                          	end
                          end
                          

                          Of cource, I define a group in method, where I draw a rectangel block, but it's independently. So first I call this method, it'll draw several blocks, and then, if is necessary (according to input file) I'll call a method for notch.

                          1 Reply Last reply Reply Quote 0
                          • A Offline
                            ado130
                            last edited by

                            Hmm, I have one main method, one method for rectangle blocks and next method for cylinders, but in every of those methods I have
                            group = Sketchup.active_model.active_entities.add_group() entities = group.entities
                            so then i.e.
                            circle = entities.add_circle(center, dir, radius)
                            or
                            entities.add_line(ptA, ptB) etc bottom = entities.add_face(ptA, ptB, ptC, ptD)
                            So this is the wrong solution?
                            If I understand correctly, I need have group = Sketchup.active_model.active_entities.add_group() only in main method and then passed as a variable to other methods?

                            1 Reply Last reply Reply Quote 0
                            • sdmitchS Offline
                              sdmitch
                              last edited by

                              @ado130 said:

                              I had never defined the group in this method previous.

                              That being the case, group must be created as a module variable(@group) or defined in the same way it is in the other methods or passed as a variable to other methods. The previous code would have created an empty group.

                              Nothing is worthless, it can always be used as a bad example.

                              http://sdmitch.blogspot.com/

                              1 Reply Last reply Reply Quote 0
                              • sdmitchS Offline
                                sdmitch
                                last edited by

                                @ado130 said:

                                Hmm, I have one main method, one method for rectangle blocks and next method for cylinders, but in every of those methods I have
                                group = Sketchup.active_model.active_entities.add_group() entities = group.entities
                                so then i.e.
                                circle = entities.add_circle(center, dir, radius)
                                or
                                entities.add_line(ptA, ptB) etc bottom = entities.add_face(ptA, ptB, ptC, ptD)
                                So this is the wrong solution?
                                If I understand correctly, I need have group = Sketchup.active_model.active_entities.add_group() only in main method and then passed as a variable to other methods?

                                You have a group containing a rectangular block that you you want to "drill" holes in. In order to do that, you have to add a group inside that group to contain the circle defining the drill hole. After exploding the group containing the circle, the original group will contain the block, the drill hole outline, and hopefully the face that is to be pushpulled to create the drill hole.

                                Nothing is worthless, it can always be used as a bad example.

                                http://sdmitch.blogspot.com/

                                1 Reply Last reply Reply Quote 0
                                • A Offline
                                  ado130
                                  last edited by

                                  So there are 2 solutions for this (I think)

                                  1. save somewhere the group where will be notch
                                  2. find that group somehow
                                    because, when I do a dril, I'm using this
                                  		group = nil
                                  		face = nil
                                  		Sketchup.active_model.entities.each{|g|
                                  			next unless g.is_a?(Sketchup;;Group)
                                  			g.entities.each{|f|
                                  				next unless f.is_a?(Sketchup;;Face)
                                  				if f.classify_point(center) == Sketchup;;Face;;PointInside then
                                  					group = g
                                  					face = f
                                  				end
                                  			}	
                                  		}
                                  

                                  2.) Exist a way how to find "my" group, where is the rectangular block, when the center point is outside that block?

                                  1 Reply Last reply Reply Quote 0
                                  • sdmitchS Offline
                                    sdmitch
                                    last edited by

                                    @ado130 said:

                                    So there are 2 solutions for this (I think)

                                    1. save somewhere the group where will be notch
                                    2. find that group somehow
                                      because, when I do a dril, I'm using this
                                    		group = nil
                                    > 		face = nil
                                    > 		Sketchup.active_model.entities.each{|g|
                                    > 			next unless g.is_a?(Sketchup;;Group)
                                    > 			g.entities.each{|f|
                                    > 				next unless f.is_a?(Sketchup;;Face)
                                    > 				if f.classify_point(center) == Sketchup;;Face;;PointInside then
                                    > 					group = g
                                    > 					face = f
                                    > 				end
                                    > 			}	
                                    > 		}
                                    

                                    2.) Exist a way how to find "my" group, where is the rectangular block, when the center point is outside that block?

                                    Perhaps

                                    if center.on_plane f.plane then
                                    	group = g
                                    	face = f
                                    end
                                    
                                    

                                    Nothing is worthless, it can always be used as a bad example.

                                    http://sdmitch.blogspot.com/

                                    1 Reply Last reply Reply Quote 0
                                    • A Offline
                                      ado130
                                      last edited by

                                      It almost works, but the result is

                                      http://i.imgur.com/RMnKEB6.png

                                      def notch(x, y, z, radiuses, depths, directions)
                                      	model = Sketchup.active_model 
                                      	countOfNotch = x.length.to_i
                                      	countOfNotch.times do |i|
                                      		
                                      		center = [x[i], y[i], z[i]].map(&;to_f)		
                                      		radius = radiuses[i]
                                      		depth = depths[i]
                                      		direction = directions[i]
                                      		
                                      		case direction
                                      			when "N"
                                      				dir = Geom;;Vector3d.new(0,0,1)
                                      			when "E"
                                      				dir = Geom;;Vector3d.new(-1,0,0)
                                      			when "S"
                                      				dir = Geom;;Vector3d.new(0,0,-1)
                                      			when "W"
                                      				dir = Geom;;Vector3d.new(1,0,0)		
                                      			when "F"
                                      				dir = Geom;;Vector3d.new(0,-1,0)	
                                      			when "B"
                                      				dir = Geom;;Vector3d.new(0,1,0)				
                                      			else
                                      				dir = Geom;;Vector3d.new(0,0,1)		
                                      		end		
                                      		
                                      		group = nil
                                      		Sketchup.active_model.entities.each{|g|
                                      			next unless g.is_a?(Sketchup;;Group)
                                      			g.entities.each{|f|
                                      				next unless f.is_a?(Sketchup;;Face)
                                      				if center.on_plane? f.plane then
                                      					group = g
                                      				end
                                      			}	
                                      		}	
                                      		gents = group.entities
                                      		active_faces = gents.grep(Sketchup;;Face);
                                      		circle_group = gents.add_group; cge = circle_group.entities;
                                      		edges = cge.add_circle(center, dir, radius);
                                      		circle_group.explode;
                                      		circle_face = (gents.grep(Sketchup;;Face) - active_faces)[0];
                                      		circle_face.pushpull(-depth)		
                                      	end
                                      end
                                      

                                      It works only if the cylinder's face is whole on the block.

                                      It "works", if I have two blocks on each other

                                      http://i.imgur.com/kAnKpQY.png

                                      1 Reply Last reply Reply Quote 0
                                      • sdmitchS Offline
                                        sdmitch
                                        last edited by

                                        In the second image, are both blocks in the same group? If so two new faces will be created and only one is being pushpulled.

                                        Nothing is worthless, it can always be used as a bad example.

                                        http://sdmitch.blogspot.com/

                                        1 Reply Last reply Reply Quote 0
                                        • A Offline
                                          ado130
                                          last edited by

                                          No, they aren't. I know it's a problem and if I will do drill into 2 blocks, they need to be in the same group. But for now, the problem is first image. It looks like, pushpull tools pushpull the block and not cylinder(drill/notch).

                                          It works!! I had a little bug in my input file.
                                          So final code is

                                          		group = nil
                                          		Sketchup.active_model.entities.each{|g|
                                          			next unless g.is_a?(Sketchup;;Group)
                                          			g.entities.each{|f|
                                          				next unless f.is_a?(Sketchup;;Face)
                                          				if center.on_plane? f.plane then
                                          					group = g
                                          				end
                                          			}	
                                          		}	
                                          		gents = group.entities
                                          		active_faces = gents.grep(Sketchup;;Face)
                                          		circle_group = gents.add_group; cge = circle_group.entities
                                          		edges = cge.add_circle(center, dir, radius)
                                          		circle_group.explode
                                          		circle_face = (gents.grep(Sketchup;;Face) - active_faces)[0]
                                          		circle_face.pushpull(-depth)	
                                          		togos = [] 
                                          		edges.each{|edge| togos << edge unless edge.faces[0] }
                                          		model.active_entities.erase_entities(togos) if togos[0]	
                                          

                                          Many many many thanks to both!
                                          Maybe a question about the second image, is it possible do it somehow?
                                          i.e.: Even if they are 4 blocks next to each (2x2 matrix) and create a hole in the middle.
                                          It occurred to me, try to apply this method twice (or more, if there will be more blocks next to each other).
                                          Or maybe 2nd solution, make a temporary group with all common groups/faces where will be a hole.

                                          Edit:
                                          So, I edited it

                                          		groups = []
                                          		i = 0
                                          		Sketchup.active_model.entities.each{|g|
                                          			next unless g.is_a?(Sketchup;;Group)
                                          			g.entities.each{|f|
                                          				next unless f.is_a?(Sketchup;;Face)
                                          				if center.on_plane? f.plane then
                                          					groups.each {|x| next if g == x}
                                          					groups[i] = g
                                          					i += 1
                                          				end
                                          			}	
                                          		}	
                                          		
                                          		groups = groups.uniq
                                          				
                                          		countOfGroups = groups.length.to_i
                                          		countOfGroups.times do |i|
                                          			group = groups[i]
                                          			gents = group.entities
                                          			active_faces = gents.grep(Sketchup;;Face)
                                          			circle_group = gents.add_group; cge = circle_group.entities
                                          			edges = cge.add_circle(center, dir, radius)
                                          			circle_group.explode
                                          			circle_face = (gents.grep(Sketchup;;Face) - active_faces)[0]
                                          			next if circle_face == nil
                                          			circle_face.pushpull(-depth)	
                                          			togos = [] 
                                          			edges.each{|edge| togos << edge unless edge.faces[0] }
                                          			model.active_entities.erase_entities(togos) if togos[0]		
                                          		end
                                          

                                          http://i.imgur.com/GDdGN4p.png

                                          It "works", but there is a problem, if I need do a drill over 3 (more) blocks.
                                          Or when I have 4 blocks, etc..

                                          http://i.imgur.com/o0xkiYw.png

                                          I found that after this circle_face = (gents.grep(Sketchup::Face) - active_faces), there are two faces, so I tried ".last", it helped, but still don't work at well.

                                          http://i.imgur.com/J8tQrBb.jpg

                                          Aby ideas?

                                          1 Reply Last reply Reply Quote 0
                                          • A Offline
                                            ado130
                                            last edited by

                                            It seems that it works. Is it correct way?
                                            Just one question, how can I delete the black lines?, or hide them.

                                            def drill(x, y, z, radiuses, depths, directions)
                                            	model = Sketchup.active_model 
                                            	countOfDrills = x.length.to_i
                                            	countOfDrills.times do |i|
                                            		
                                            		center = [x[i], y[i], z[i]].map(&;to_f)		
                                            		radius = radiuses[i]
                                            		depth = depths[i]
                                            		direction = directions[i]
                                            		
                                            		case direction
                                            			when "N"
                                            				dir = Geom;;Vector3d.new(0,0,1)
                                            			when "E"
                                            				dir = Geom;;Vector3d.new(-1,0,0)
                                            			when "S"
                                            				dir = Geom;;Vector3d.new(0,0,-1)
                                            			when "W"
                                            				dir = Geom;;Vector3d.new(1,0,0)		
                                            			when "F"
                                            				dir = Geom;;Vector3d.new(0,-1,0)	
                                            			when "B"
                                            				dir = Geom;;Vector3d.new(0,1,0)				
                                            			else
                                            				dir = Geom;;Vector3d.new(0,0,1)		
                                            		end		
                                            		
                                            		groups = []
                                            		i = 0
                                            		Sketchup.active_model.entities.each{|g|
                                            			next unless g.is_a?(Sketchup;;Group)
                                            			g.entities.each{|f|
                                            				next unless f.is_a?(Sketchup;;Face)
                                            				if center.on_plane? f.plane then
                                            					groups.each {|x| next if g == x}
                                            					groups[i] = g
                                            					i += 1
                                            				end
                                            			}	
                                            		}	
                                            		
                                            		groups = groups.uniq
                                            		
                                            		countOfGroups = groups.length.to_i
                                            		countOfGroups.times do |i|
                                            			group = groups[i]
                                            			gents = group.entities
                                            			activeFaces = gents.grep(Sketchup;;Face)
                                            			circleGroup = gents.add_group; cge = circleGroup.entities
                                            			edges = cge.add_circle(center, dir, radius)
                                            			circleGroup.explode
                                            			circleFace = (gents.grep(Sketchup;;Face) - activeFaces).last
                                            			next if circleFace == nil
                                            			normal = circleFace.normal
                                            			activeFaces = gents.grep(Sketchup;;Face)
                                            			circleFace.pushpull(-depth)
                                            			deleteFace = (gents.grep(Sketchup;;Face) - activeFaces)[0]
                                            			if deleteFace.normal == normal then
                                            				deleteFace.erase!	
                                            			end
                                            		end
                                            	end
                                            end
                                            
                                            1 Reply Last reply Reply Quote 0
                                            • 1
                                            • 2
                                            • 3
                                            • 2 / 3
                                            • First post
                                              Last post
                                            Buy SketchPlus
                                            Buy SUbD
                                            Buy WrapR
                                            Buy eBook
                                            Buy Modelur
                                            Buy Vertex Tools
                                            Buy SketchCuisine
                                            Buy FormFonts

                                            Advertisement