How to cut from object
-
Learn this http://ruby.sketchup.com/
Let's assume you understand how entities-context separates geometry.
Let's assume that the rectangular block form is inside a group's entities.
In Pro you'd make another temporary group containing the cylinder, position it, and subtract that from the block, making a new group in the process, that has the notch cut out.In Make, to subtract geometry to make the notch, consider this...
Add a temporary cylinder group inside the block group's entities.
Adding the group, then adding a circle into it, and then adding a face to that.
You save the normal of the circular face for later.
Now pushpull the circular face along a single line - with its length set as the width of the block, to make the 3d cylinder.
Make a collection of the faces in the block group and use entities intersect_with(...), using the cylinder group as the 'cutter'.
It should have 'cut' the form of the notch into the block's faces [return as the result of the operation]
Now you can erase the cylinder group.
You can see the cuts.
From the returned new faces one will have the same normal as the cylinder's initial circular face, i.e. the first of the two half-moon faces.
Use pushpull on that to subtract the notch from the block's geometry...If you want to subtract random sized objects from each other, then its more involved.
Consider doing the intersect_with twice, once putting the new geometry into the block and next into the cylinder.When testing it do it in steps so you can see what is getting done at each one...
-
About groups, I'm not sure, if I'm doing it right.
Rectangel blocks:group = Sketchup.active_model.entities.add_group entities = group.entities ptA = [x[0+i*8], y[0+i*8], z[0+i*8]].map(&;to_f) ... entities.add_line(ptA, ptB) ... bottom = entities.add_face(ptA, ptB, ptC, ptD) ... bottom.material = blockMaterial[i] ... bottom.back_material = blockMaterial[i] ...
Similarly cylinders (just using entities.add_circle).
Is it a right way?Now for make a drill (not a notch), I need to find a face, so I'm trying sth like this
model = Sketchup.active_model entities = model.entities face = nil entities.grep( Sketchup;;Group ).each{|e| puts "Group #{e.name.empty? ? e.definition.name.inspect ; e.name.inspect};" faces = e.definition.entities.grep( Sketchup;;Face ) if faces.empty? puts " +-- No faces in group!" else for f in faces if f.classify_point(center) == Sketchup;;Face;;PointInside then face = f end end end }
It works - it finds right face, but after, when I execute code from here http://sketchucation.com/forums/viewtopic.php?t=19556, the result is, that the block disappear and stay just a circle edge.
When I add group.explode at the end of block for making rectangles and cylinders works fine this:
entities.each { |entity| # Get all entities if entity.typename == "Face" then # Get all faces from entities result = entity.classify_point(center) if result == Sketchup;;Face;;PointInside then # Get our face face = entity end end }
If I think properly, gourp.explore deletes that group, but I need it for use in intersect_with.
So, is this process right: delete that group via group.explode (for making drills) and before making notch, create new group of block and temporary cylinder?I learn from this https://www.sketchup.com/intl/en/developer/docs/ and forum.
Thanks
Edit:
baseGroup = 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) next if f.classify_point(notchPoint) == Sketchup;;Face;;PointInside baseGroup = g } } baseTrans = baseGroup.transformation cutGroup=entities.add_group cutGroupEntities=cutGroup.entities dir = Geom;;Vector3d.new(-1,0,0) cutCircle = cutGroupEntities.add_circle(notchPoint, dir, cylinderRadius[0]) cutFace = cutGroupEntities.add_face cutCircle cutNormal = cutFace.normal cutFace.reverse! cutFace.pushpull cylinderHeight[0] cutTrans = cutGroup.transformation cutGroupEntities.intersect_with(false, cutTrans, baseGroup, baseTrans , true, baseGroup) cutGroup.explode
In baseGroup variable I need to find a group where I'll do a notch - and yeah, it does not work at well (now I have just 1 block, so just 1 group, but later I'll have a lot of blocks/groups).
Then I do a temporary cylinders and trying execute intersect_with, but I'm not sure if is it correct. Can you help me?Thanks a lot.
-
TIG, please, can you help me with that.
-
Let's step back.
Let's agree what is is you want to do...
In words, not code...
I think it's this...You want to add a cylindrical hole into grouped block.
Let us assume the radius and depth are fixed - once you have something working you can always add an inputbox to get parameters...Let's assume you have a rectangular block already made and it's a group.
Let's also assume that the hole you want to add to this is centered on its top and then passes right through the block.So it's relatively straightforward...
model = Sketchup.active_model group = model.selection.grep(Sketchup;;Group)[0] ### first group in selection gents = group.entities bb = group.bounds ct = bb.center max = bb.max ctop = ct.clone.transform!(group.transformation.inverse) ctop.z = max.z ### move onto top dist = bb.depth ### full depth rad = 10.0 seg = 12 edges = gents.add_circle(ctop, Z_AXIS, rad, seg) ### note axis fac = nil edges[0].faces.each{|f|fac = f unless f.loops[1]} fac.pushpull(-dist)
Now all you have to do is extend this to your real needs ?
-
Thanks for a reply.
Yes, I have all rectangular blocks grouped, radius and depth are fixed (from a txt file).
I'm not sure, but that code do a drill in center of some rectangular block, but what I need to do is make a notch (like in pic in my prev post) - the center position of this imaginary cylinder may not be on that block.
Do you know what I mean? -
OK.
I only gave the example to show that is a relatively few lines of code you can drill a hole.
I centered it on the top because that was easy to do.
If you want it elsewhere you need to decide how the point is decided upon.
You need to implement a Tool class to allow you to to actually capture a picked point...
Perhaps do that a separate process to see how it works, then combine it with your 'driller'.
I punched it through the block as it was easy to do.
However, you could ask the user with an inputbox for both depth [and radius/diam].
Then instead of using the block's size to determine the through punch use the entered depth instead... -
I rewrote your code to do a drill and it looks fine (works
)
def drill(x, y, z, radius, depth, direction) countOfDrills = x.length.to_i countOfDrills.times do |i| model = Sketchup.active_model entities = model.entities case direction[i] 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 center = [x[i], y[i], z[i]].map(&;to_f) 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 } } gents = group.entities edges = gents.add_circle(center, dir, radius[i]) ### note axis fac = nil edges[0].faces.each{|f|fac = f unless f.loops[1]} normal = face.normal other_entities = face.all_connected reversed_normal = normal.reverse circleface = nil for other in other_entities if other.typename == "Face" and other.classify_point(center) == Sketchup;;Face;;PointInside then circleface = other end end for other in other_entities if other.valid? and other.typename=="Face" then if reversed_normal.samedirection? other.normal then point_on_other_face = center.project_to_plane other.plane if other.classify_point(point_on_other_face) == Sketchup;;Face;;PointInside then dist = point_on_other_face.vector_to(center).length end end end end if depth[i] != 0 then dist = depth[i] end fac.pushpull(-dist) end return end
But still, I'm not sure how to do a notch.
I can't add a inputbox for user, because all of my input data are from txt file - there I have x y z positions of imaginary center then radius, depth and if I need to know direction also. -
Some ideas...
Before you do anything, get a collection of all of the existing [active] edges.
active_edges = model.active_entities.grep(Sketchup::Edge)
We need that later...
If the punching-face is not wholly on the top surface then some of new edges will not intersect or only partially intersect with the 'block'.
So add the circle [say], then check its edges for their faces and save the first withedge.faces[0]
for later...
Before doing anything else...
new_edges = model.active_entities.grep(Sketchup::Edge) - active_edges
Then delete those in new_edges which have no faces, using...
! edge.faces[0]
Finally pushpull the new face by the desired depth...
?? -
active_edges = model.active_entities.grep(Sketchup;;Edge) group = Sketchup.active_model.active_entities.add_group() gents = group.entities edges = gents.add_circle(center, dir, notchRadius[0]) first = edges[0].faces[0] new_edges = model.active_entities.grep(Sketchup;;Edge) - active_edges
Now, should I delete those in new_edges which have no faces, but I'm not sure, how to find them. And you said, thath I need to check its edges for their faces , what do you mean there?
Thanks
-
After making the array of edges before you start [active_edges]...
Then after adding your new edges etc...
new_edges = model.active_entities.grep(Sketchup::Edge) - active_edges new_edges.each{|edge| edge.erase! unless edge.faces[0] }
There is a more efficient way, collecting then deleting them at the end but since you'll only have a few to erase it's probably not worth it...
But FYI here it is...
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]
-
So, is this correct? Now just pushpull the new face (which?) and it should be done.
active_edges = model.active_entities.grep(Sketchup;;Edge) group = Sketchup.active_model.active_entities.add_group() gents = group.entities edges = gents.add_circle(center, dir, radius[i]) first = edges[0].faces[0] puts first 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]
-
You are a "bit over your head"...
As you don't seem to understand what you are trying to do at each step...All the code we have been talking about recently is to erase the unwanted edges if the new circle is not wholly within the targeted face.
Earlier code - which already worked - will find the face associated with any new edges which you just added and which have a face... and then you just do a pushpull on that that...
-
Yeah, you're right, I don't get it completely.
-
save now active edges
active_edges = model.active_entities.grep(Sketchup::Edge)
-
make a new group and add there circle
group = Sketchup.active_model.active_entities.add_group() gents = group.entities edges = gents.add_circle(center, dir, radius[i])
-
select edges, which makes this circle
new_edges = model.active_entities.grep(Sketchup::Edge) - active_edges
-
erase the unwanted edges
togos = [] ### empty array new_edges.each{|edge| togos << edge unless edge.faces[0] } model.active_entities.erase_entities(togos) if togos[0]
-
now last step, that you said; find the face associated with any new edges which I just added and which have a face
fac = nil new_edges.faces.each{|f|fac = f unless f.loops[1]} fac.pushpull(-10)
Is it correct now? I'd like to learn it (understand it), not only copy and paste the code.
-
-
We are adding the circle onto an existing face [you never showed that step or set up the values for the circle itself ??]
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)
Where center is a point on the face [or the plane of the face],
dir is the face.normal, radius is the circle's radius [NOT an array it's a dimension], and segs is the number of sides the circle will have.When you draw the circle's edges there are three possible outcomes...
a) The circle intersects fully with the face and then all of those edges have two faces - so there's nothing to tidy up - next you can find/use the new face supported by one of the circle's edges and pushpull it to form the hole or notch [i.e. no face.loops[1]].
b) The circle misses the face completely, so all of its edges are faceless, so they all need deleting and the code exits.
c) The circle straddles the edge of the face. Now some edges are faceless and need deleting. At least one other circle's edge will support two faces and one of them is new - again this can be used for the pushpull...The use of the togos array to remove any faceless edges is OK.
The finding the face fac and pushpulling it is OK.Unfortunately since your code added the circle into a group no new edges are found in the active_entities. So nothing to find with a face etc, so nothing to pushpull.
I suggest you first get it working in the active_context, then learn how to do it ALL inside a group.entities context to separate the geometry from the rest of the model and make finding new stuff easier...
-
I know, that the circle's radius is not an array, but that's not a whole code.
def notch(x, y, z, radius, depth, direction) countOfNotch = x.length.to_i countOfNotch.times do |i| model = Sketchup.active_model center = [x[i], y[i], z[i]].map(&;to_f) case direction[i] 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 active_edges = model.active_entities.grep(Sketchup;;Edge) edges = Sketchup.active_model.active_entities.add_circle(center, dir, radius[i]) 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.faces.each{|f|fac = f unless f.loops[1]} fac.pushpull(-depth) end end
Is it necessary set the seg?, I think there is a default value.
So I add circle into active_entities.For point a) I've a separate code (the above code) - make drill; I use it, when I know, that the drill (circle) intersects fully with the face (i.e. block).
-
You are right in that the 'segments' in
add_circle
will default to24
if you do not pass a final argument, but you might well want to make the circle less segmented if it's very small or more segmented if it's very large - it depends on what you are to do with it next...
If it's in 3d-printing look up 'versine' as the segment's length should never be 1/1000" or less, and of course this depends on the radius and number of segments...A circle's radius is a dimension, but your code method seems to receive an argument, which you name
radius
but you then treat it as an array, to takeradius[i]
withi
derived from the notch counter...
So it is an array ?
If so, then I think it'd be clearer for anyone reading it [including yourself] to call it sayradii
,rads
orradiuses
and then setrad = rads[i]
before using it...
Also yourdirection
sees to be an array too !Without seeing all of your related code it is all but impossible to deduce what you are doing at each step.
Common practice is to name a 'collection' - like an array - plural and then take an element in it named in the singular...
rads >>> rad
dirs >>> dir
pts >>> pt
edges >>> edge
faces >>> face
etcAnother minor thing...
model = Sketchup.active_model
can be set at the start of the method, resetting it at each iteration will slow its processing up slightly.If you have other code checking that the drilled hole is wholly on the face so there is no 'part-circle' side holes, then the new edge collection and unfaced edge deletion is superfluous, since every edge of the circle is faced... try ### out parts to see what's not needed...
-
Yes, radius, depth and direction are arrays with the float numbers.
In another part of code I only read txt file line by line and split it into this arrays.I've two arrays, in first array are "drills" (coordinates, depth, etc), that are whole on the face, and second array, where I don't know if the imaginary center of notch is on the face or not.
So, function for drill works fine, but still I've problem with notches.The whole code has 460 lines, so .. if you want, I can send it via PM.
Thanks for the advice of the name.
In future
if it will work, I wanna buy Pro version and do sth like client-server. This script will run on the server, and i.e. you like a client send points (These can be generated from the C# program) and send to server. He process them, sent back Sketchup model to the client and client can open it with Sketchup View.
So I've one more question. Is this possible?, I think the part where Pro version can automaticaly open ruby script and save the model. -
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...
-
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 itanother 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:
- is possible do a fillet (radius) on rectangel blocks?
- 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..
-
@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 itanother 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:
- is possible do a fillet (radius) on rectangle blocks?
- 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...
-
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... -
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...
Advertisement