sketchucation logo sketchucation
    • Login
    1. Home
    2. Cleverbeans
    3. Posts
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info
    C
    Offline
    • Profile
    • Following 0
    • Followers 0
    • Topics 0
    • Posts 41
    • Groups 1

    Posts

    Recent Best Controversial
    • RE: Ruby autosplit edge?

      The boolean intersection operation as I'm sure you've already discovered can be really flaky when working with complex geometry, and that's more or less been the case in every CAD package I've worked with. It's a tough algorithm to implement and for anything non-linear it will be imperfect.

      Also, when you add an edge to the entities collection a lot is going on behind the scenes. Information about which face the edge belongs to, what the adjacent edges are, and their vertices all has to be stored in a table and updated on each operation. That overhead should be avoided when possible, and it can be here.

      Calculating the intersection of two lines is however, really fast computationally, and it's also fast to check if it's in the segment defined by your two points as well. Thankfully you can avoid comparing each edge by using the Bentley-Ottmann Algorithm or one of it's variants if the segments are coplanar. It runs in about n log n time which sure beats the n^2 time of the naive approach. If your edges are not coplanar than you will have to test for that as well, however that's not to bad either since if your segments are skew then the four points form a tetrahedron with non-zero volume, so you simply calculate the volume and if it's 0, they're coplanar so you can sort them out and calculate the intersections from there. I would expect this to be orders of magnitude faster than using the intersect_with method.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: Ordering 3dpoints ?

      First it's important to note that such a face may not be unique. Say for example if you take the points (0,0),(1,1),(-1,1) and (0,-1) then every ordering that starts with (0,0) will either be clockwise or counter-clockwise but depending on way you put the other points in will get a different face. If you can always count on the face being convex however I believe this ambiguity vanishes.

      To order them appropriately, you can first choose any three points, and just put them in any order, which will either be clockwise or counter-clockwise. Now you want to add the fourth point into the order at the correct spot so that you don't get that intersection issue. You can convince yourself pretty easily that if you draw a triangle, and take a point outside that triangle you basically want to connect it to two points without crossing any of the existing lines on the triangle. From here, it's easy to see that you want to connect it to the two points which are closest to it. Here is some code which will return the points in the desired order under the conditions given.

      
      def order_points(points)
         #Given an array of four coplanar, convex point3d objects,  returns the points in convex order
      
         pt = points[0]
         triangle = points[1..-1] #the triangle which is already either clockwise or counter-clockwise
      
         distances = points.map{|x| pt.distance_to(x)}
         i = distances.index(distances.max()) #this finds the index of the point which is furthest away in the points array
      
         if i == 0 then
             return triangle[0..1] + [pt] + triangle[2]
         elsif i==1 then
             return triangle + [pt]
         else
             return [pt] + triangle
         end
      end 
      
      

      I think that should do the trick. πŸ˜„

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: How to debug?

      I've gotten into a few habits with debugging that I've found helpful.

      Firstly, as some folks have already mentioned I break my code up into small bits as much as possible. I've got a module that around 1000 lines of code and the longest method is only 7 lines, and it was a dream to debug. It's really easy to spot what's going wrong when it's such a tiny box. Also, there is no reason to be wary of writing a method with only a single line of code in it's body if it will meaningfully capture some logic in your overall code. This way the method names become a part of your documentation, and allow you to follow the logic easily and it naturally describes the problem you're working on.

      Secondly, anytime I'm going to be writing a complicated method I will generally write my tests before I write my code. This often helps me to clarify the design, build some of the documentation, and it can speed up the debugging process considerably. I will also keep my test code even if I never use it again. You'll see a lot of coders type a few quick lines in the console to test something while they're writing a method and then throw it away. I do the same thing, but I just copy/paste it into another file and hold onto it. I never end up using almost all that code, but for the few times I do when

      Thirdly, I will test my code after almost every change. There is no harm in testing the method after changing just a single line of code, particularly when you're working in a scripted language like Ruby and don't have to recompile. I found that if I deferred debugging till I had "something worth running" I'd often find a chain of bugs which would obscure the real error behind the scenes. You catch a lot of little things like typos, missing brackets, type mismatches and such which are easy to quash. Also, I find it's easier to debug what I'm doing while it's fresh in my mind. If I find a semantic error in code I wrote even an hour earlier it's not fresh and I have to figure out what I was doing at that point again to see where it went wrong.

      Fourthly, I avoid state data as much as possible. Anytime declare a global, class, or instance variables you open up the possibility of side effects in your code which can make debugging a nightmare. I would also tend to avoid methods with a bang over their un-banged counterparts, so I would almost always use Array.map instead of Array.map! unless I had good reason to do so. State data exists for a reason, and they're good reasons but my experience tells me they're often used without any justification other than familiarity with procedural languages like C, Basic or Pascal. It's actually possible to write every program imaginable without any state data, which may have costs in runtime, modularity, and readability but it at least gives the sense that you can in fact avoid them and if it makes your code easier to debug and maintain that's often a good trade. Also, I never make state data private or protected while writing my code. It's much easier to debug this way since you can sniff their values, and I can always go back and make them private or protected at a later time. This applies to both variables and class definitions as well.

      Finally, I read my documentation when I'm debugging. If I'm coming back to a project where new features have been introduced and suddenly I've got unintended behavior it's really nice to have a clear understanding of what the codes purpose is, why I chose that particular implementation over others, and some of the bugs I'd previously squashed in the code. I really hate fixing one bug only to introduce a new bug I had previously fixed and just forgotten about by making the same mistake again. A one-liner saying "this prevents and off-by-one error in the loop that follows" can be really handy. Also, I consider variable names as documentation. It's easier to figure out what "for point in points" means when compared to "for pt in pts". I also prefer "and" to "&&" and "not" to "!" for the same reasons, and I would rather use a for...in loop to the .each method. The more expressive your code, the easier it is to debug.

      Also, it can be handy to familiarize yourself with a few different types of common bugs. It's easier to detect a bug when you're getting odd behavior if that behavior resembles what you'd expect from a particular type of bug and you also begin to build up a set of techniques to track them down and fix them.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: Has anyone successfully added dynamic 3D model to website?

      ICEVision supports Sketchup files as well as a variety of other formats.

      http://icevision.ice-edge.com/features.jsf

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: Loading a second set of points to draw lines.

      Well I can see the problem with your translation starting at line 234.

      
      pt=Geom;;Vector3d.new()
      tr=Geom;;Transformation.translation(pt)
      
      

      Firstly, the vector stored in pt is the zero vector, and that means that the tr transformation is the identity transformation, so when this transformation is applied you should expect nothing to change. From the sounds of it you're drawing the object at the origin, and simply want to translate it to the desired point. To do this you'll need the desired point to properly initialize the vector. Once you've identified the point you'd like to move your object to you can get the appropriate vector by using ORIGIN.vector_to(point) which returns the vector. From there you can create the translation transformation as you've done above.

      It's also unclear to me why you're grouping and exploding the curves so much. this may be introducing some of the problems you're having with the transformation. From the looks of it you're trying to work around the fact that the Curve entity does not have a transform method. It would be better to simply defer the creation of the curve until you need it, and work on the points directly. The note suggests you're concerned with interaction between various curves, but if you've got the points defined uniquely in the "co" variable in the create_offset_cantenary I'm not sure why this is an issue. Here is how you can transform the points in "co" simply.

      
      co.map!{|pt| pt.transform!(tr)}
      
      

      Finally, and most importantly, you have declared a gratuitous number of global variables. As a rule you should not introduce global variables unless you have absolutely no reasonable alternative. If you have multiple methods manipulating the same global variables you open yourself up to side-effects in your code which are notoriously difficult to debug. It is much better to use parameters, return values and local variables to control the scope and flow of your code instead of global variables. This way if a bug is introduced in your code you can easily isolate it to a particular method which can save you days of frustration. Your code will also be much more modular, and by extension reusable. That way on future projects you don't have to reinvent the wheel. If you absolutely have to introduce a global you should try and encapsulate it within an object instead so that you can localize its scope as much as you possibly can. If I was asked to debug your code in it's current condition, I would find it faster to gut it and start from scratch keeping only the constants used for the co and cs variables, the string literals, and the comments. On the plus side, your code would make for a great spaghetti dinner. πŸ˜‰

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: How to compare points on a plane?

      So I ran some test this morning, and surprisingly the code I presented appears to be the fastest. Here is the code I used to test it.

      First Method

      
      def first_test(pts)
          xmax = pts[0].x
          ymax = pts[0].y		
          for p in pts
      	if p.x > xmax then
      	    xmax = p.x
      	end
      	if p.y > ymax then
      		ymax = p.y
      	end
          end
          return xmax,ymax
      end
      
      

      Second Method

      
      def second_test(pts)
          return pts.inject([pts[0],pts[0]]){|mem,pt| [ mem[0].x>pt.x ? mem[0];pt, mem[1].y>pt.y ? mem[1];pt ]}
      end
      
      

      Third Method

      
      def third_test(pts)
          xmax = pts.max{|a,b| a.x <=> b.x}
          ymax = pts.max{|a,b| a.y <=> b.y}
          return xmax,ymax
      end
      
      

      The Testing Routine

      
      def the_test()
          pts = []
          for x in 0...10000000
      	pts.push(Geom;;Point3d.new(rand,rand,rand))
          end 
          start = Time.now()
          first_test(pts)
          finish = Time.now()
          interval = finish.to_i - start.to_i
          puts("First Time; " + interval.to_s)
      	
          start = Time.now()
          second_test(pts)
          finish = Time.now()
          interval = finish.to_i - start.to_i
          puts("Second Time; " + interval.to_s)
      
          start = Time.now()
          third_test(pts)
          finish = Time.now()
          interval = finish.to_i - start.to_i
          puts("Third Time; " + interval.to_s)
      
          return "Complete."
      end
      
      

      Results

      
      qt()
      First Time; 18
      Second Time; 61
      Third Time; 56
      Complete.
      
      

      Truth is stranger than fiction sometimes.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: [Plugin] bim-tools 0.13.4(june 22, 2015)

      An IFC exporter in Sketchup is non-trivial since all the metadata would need to be custom coded, so it basically boils down to reimplementing the IFC framework within Sketchup. The main issue is identifying what the geometry is intended to be. I expect you could do it for a limited scope such as floor plans with a bit of effort.

      posted in Plugins
      C
      Cleverbeans
    • RE: Help with componentdefinition=definitions.load name

      @honoluludesktop said:

      More precicely:

      1. I saved the transformations of a set of components.
      2. Then deleted the components from the model.
      3. Created a new definition from a new .skp.
      4. Created components with the new definition and the saved transformations.

      This cnt_instance.definition.save_as(File.dirname(model.path)+"\\null") returned, Error: #<TypeError: reference to deleted ComponentInstance>, where cht_instance is the deleted component.

      I also tried definitions.purge_unused without success.

      Try switching steps 2 and 3. Once you delete the component referring to it will throw the error you're getting. If you require a reference to the instance in order to property do step 4, then add the new components to the model first and delete the old components afterwards.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: How to compare points on a plane?

      It's nice to see that the enumerable class supports so many functional methods. Thanks for pointing it out.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: Something new for SU ruby one year after.???

      What you're describing is exactly how the QT Framework works, it's cross platform, and the Ruby bindings already exist. It also enjoys the advantage of being open-source, although the QT Designer IDE is proprietary. So just as you can write and compile C# code for the .NET framework without Visual Studio, the task is greatly simplified with the native IDE.

      GUI work is well outside my day to day development however, so I'm not sure if it could be made to work with the Sketchup API. I'll defer such decisions to those better qualified to make them. πŸ˜„

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: Something new for SU ruby one year after.???

      The main difficulty I see with a GUI builder is ensuring it's cross platform. Other CAD systems I've work with that provide this feature are using a prepackaged variant of Visual Studio which is Windows only. Creating a GUI builder from scratch is a non-trivial project and well outside the scope of the Sketchup development team so I would think something custom built for Sketchup to be highly unlikely. The only reasonable possibility I can think of is perhaps fitting into the QT framework since Ruby already has bindings and it's platform independent allowing development in QT designer. That or a community built solution, but I'm not confident our development community has the manpower for such a project. Having done some API work in Revit which comes with GUI building tools but which is so prohibitive in how it allows developers to interact with the application that it's nearly useless. It's a trade I'm happy to make. It's certainly not as strong as the AutoCAD API, however it's not entirely a fair comparison since AutoCAD has had decades longer to mature by comparison. Even then, I find the openness of Sketchup's API to be very refreshing in an industry which has historically committed itself to proprietary shenanigans.

      Beyond that the only feature I'd like to see in an IDE is code completion for Sketchup Objects, but that is much lower on my wish list compared to say... fixing observers. I was also very disappointed to see solid operations restricted to Pro-only versions, which is presumably to prevent the community from writing replacement tools for the pro-only features related to solid manipulation. I would have preferred to see Sketchup differentiate itself by the quality of it's tools rather than artificial restrictions on the features provided as they did with dynamic components.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: How to compare points on a plane?

      @dan rathbun said:

      Why use slow interpreted Ruby to wite your own interator, when the example I gave above, uses the compiled C interator that all Array objects inherit from module Enumerable?

      In general I find it superior to use code I understand to code which I find opaque. Ruby's syntactic quirks are still something of a mystery to me, and I didn't want to present code I couldn't explain. I'll definitely want to take advantage of the method you've presented however since I'm doing similar things in my current project. On that note, it appears there are two independent iterations over the same collection occurring here.

      
      max_x = new_points.max {|a,b| a.x <=> b.x }
      max_y = new_points.max {|a,b| a.y <=> b.y }
      
      

      Is there a means of bundling them into a single traversal?

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: How to compare points on a plane?

      Since you have the vectors defining your x and y directions, you can get a third by taking their cross product giving you to get the vector normal to the plane giving you a custom z as well. For the purposes of finding the points with maximal custom x and y values you can choose any point in the plane as an origin since the relative position of the other points will not change depending on your selection.

      The Transformation.new method can take an origin and three axis as arguments. This transformation can be thought to take the world x,y,z axis and turn them into the custom axis your plane defines. The idea is to transform your points using the inverse of this transformation, which will lay them neatly in the world x-y plane. Then you can find the maximum by comparing the x and y values in the usual way, and then do the transformation on the points once you've identified them to retrieve the points you want. Here is a function which you can tailor to your needs.

      
      def custom_max_x_and_y(custom_x,custom_y,pt_array)
          custom_z = custom_x.cross(custom_y)
          trans = Geom;;Transformation.new(pt_array[0],custom_x, custom_y, custom_z)
          new_points = pt_array.map{|pt| pt.transform(trans.inverse)}
          max_x = new_points[0]
          max_y = new_points[0]
          for pt in new_points
              if pt.x > max_x.x then
      		max_x = pt
      	end
      	if pt.y > max_y.y then
      		max_y = pt
      	end
          end
          return max_x.transform(trans), max_y.transform(trans)
      end
      
      
      posted in Developers' Forum
      C
      Cleverbeans
    • RE: Expanding Cube concept

      Perhaps the guess_target method for the view object could be used? I'm imagining adjusting the view to look out along each of the normal vectors for the cube, guessing the target, then using that entity reference to calculate the required distances. Seems it might work for simple models, however there are complications when rooms are not rectangular.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: [Code] Image: definition, transformation, transformation=

      I think the methods TIG described gives the right idea, however I'm not sure there is a problem with the code as provided. It seems as though you can transform the image in any meaningful way with a correctly articulated transform, and if it is not "working" as the user expects that be an issue with the user's understanding of how transforms work rather than with the code itself.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: [Code] Image: definition, transformation, transformation=

      So the trouble is to get the transformation which will flip the image? Does this transformation give the behavior you're looking for? If so, do you want a transform constructed by Geom::Transformation.scaling(-1) method to produce the same behavior as this transform?

      tr = Geom;;Transformation.new([-1,0,0,0,0,-1,0,0,0,0,-1,0,0,0,0,1])
      
      posted in Developers' Forum
      C
      Cleverbeans
    • RE: Rotating objects in space

      When dealing with rotations in 3D I think it's a common misconception to consider the "direct" rotation as the only rotation since in 2D there is only ever one rotation to move a vector to point towards another. Say for example you wanted to rotate the vector (1,0,0) to the vector (0,1,0). I think most people would envision the rotation as holding the z-axis fixed the path traced by the vector as being a quarter circle in the x-y plane. However there are infinitely many rotations that will take one vector to the other!

      For a simple one, consider looking down at the origin from the point (0.5,0.5,0). From here, you can rotate 180 degrees around the (0.5,0.5,0) vector and it will achieve the same result of moving (1,0,0) to (0,1,0). Another somewhat harder rotation to see is rotating by 120 degrees around the vector (1,1,1) which is equivalent to moving each axis to another axis. In each of these cases the z-axis behaves differently under the rotation so this is where the complication in your original code stems from.

      If the behavior your looking for is the direct rotation than the suggestion given by TIG is the correct one, and should provide the expected behavior.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: [Code] Image: definition, transformation, transformation=

      I believe you can detect a negative scaling by checking the determinant of the upper left 3x3 block matrix since all negative scalings have negative determinants unless they are simply a rotation. Here is some code.

      
      def isflipped?(tr)
          m = tr.to_a()
          det = m[0]*m[5]*m[10] + m[1]*m[6]*m[8] + m[2]*m[4]*m[9] - m[2]*m[5]*m[8] - m[0]*m[6]*m[9] - m[1]*m[4]*m[10]
          if det < 0 then
              return true
          end
          return false
      end
      
      

      Let me know if that detects the transformations you're trying to modify. However I think there is a built in ambiguity here, if you're going to interpret negative scaling as a "flip" then what direction are you flipping? I think you could rotate the image around either of the two center lines and achieve a flip but then the question is which one would you expect when you do a -1 scaling transformation? Can you clarify how the current behavior differs from the expected behavior?

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: Application Observer

      Just have your plugin launch the website when it's loaded by including the function call in the script file. Something like this should work.

      
      def launch_web_dialog
      #insert relevant code here
      end
      
      launch_web_dialog()
      
      

      Then when your plugin is loaded it will launch the dialog.

      posted in Developers' Forum
      C
      Cleverbeans
    • RE: Transformations

      @zitoun said:

      I have no clue what these are yet, but I guess I somehow can retrieve my rotation matrix from such component...

      Note that a transformation has the .to_a method as well which gives you exactly the information you're looking for. The array it returns is 16 floats for the four by four matrix which defines the transformation from the components defining coordinate system to the current coordinate system. Since you mentioned quaternions I'll assume you're familiar with with the essentials of matrices and linear transformations. First, we can interpret the scaling and rotation of a component as the action of a matrix on the basis vectors of the component definition. Say for example you define your component relative to the standard basis of x=[1,0,0], y=[0,1,0], z=[0,0,1]. Rather than keeping these vectors separately, you can just encode them as the 3x3 identity matrix, and then define all the entities in the component relative to this matrix. From here, any sort of rotation/scaling can be interpreted as a change of basis. The trouble of course is that any linear transformation preserves the zero vector, so you can't model a translation in this way. This means to fully describe the relative coordinates you require a an equation of the form Ax + b where A is an invertible 3x3 change of basis matrix and b is the translation vector with x being the "defining" vector of some entity internal to the component.

      This is where the clever bit comes in. You can model an affine transformation of the form Ax + b as a single matrix transformation by embedding it into a space with one higher dimension. This is why the transformation.to_a method returns a 16 entry array, it's a 4x4 matrix with the first four entries being the first column, the second four entries being the second column and so on. It's easy enough to extract the original 3x3 matrix and the translation vector as well, the "upper-left" 3x3 block matrix is exactly the change of basis matrix A, and the final column vector is of the form [b, 1] where b is the translation vector. From here extracting the rotation matrix is a simple matter of matrix algebra since any change of basis matrix A can be decomposed as A = QS where Q is a rotation matrix and S is a diagonal matrix representing the scaling of each axis.

      It's worth getting to know the 4x4 representation if you haven't before since it's also the way the OpenGL standard models the objects. The ability to model vector addition as matrix multiplication is only one of the reason they do it, the other being that when you're rendering perspective views rather than simply orthographic views the value of the bottom right entry plays an important role in correcting the scale of the transformation after applying the perspective matrix.

      posted in Developers' Forum
      C
      Cleverbeans
    • 1
    • 2
    • 3
    • 2 / 3