diff --git a/skp2ocli/README.md b/skp2ocli/README.md new file mode 100644 index 0000000..3e9ee71 --- /dev/null +++ b/skp2ocli/README.md @@ -0,0 +1,73 @@ +# OGREE TOOL: Sketchup to OGREE Converter Plugin + +This is a guide for the plugin OGREE Converter for Sketchup PRO 2023 + +- [OGREE TOOL: Sketchup to OGREE Converter Plugin](#ogree-tool-sketchup-to-ogree-converter-plugin) + - [Usage](#usage) + - [Compile](#compile) + - [Installation](#installation) + - [Remove](#remove) + - [Code details?](#code-details) + + +## Usage +After installing the plugin, you simply: +* Select the entity that you want to know its details +* Open the drop down menu **Extension** and click **Generate OCLI files** like shown in the image below +![first step usage](./img/usage-1.png) + +## Compile +The plugin is written in Ruby, but in order for plugins to be registrable in Sketchup PRO 2023, you **must** compile the code to a **.rbz** compressed file. + +To do that, you must : +1. Create a **.zip** compressed file that contains all the **.rb** files of the plugin +2. Change the compressed file extension to **.rbz** + +*Optional*: You can use the batch script below to automate the compilation of the plugin +```bash +@echo off + +rem Define the directory to zip, a directory that contains the implementation of the plugin +set "dir_to_zip=skp2ocli" + +rem Define the file to include, a file that contains the declaration and metadata of the plugins +set "file_to_include=skp2ocli.rb" + +rem Build the zip file +set "zip_name=skp2ocli.zip" +if exist "skp2ocli.rbz" ( + del /q "skp2ocli.rbz" + echo Deleted existing zip file: skp2ocli.rbz +) +PowerShell -ExecutionPolicy Bypass -NoProfile -Command "& { Compress-Archive -Path %dir_to_zip%* -DestinationPath %zip_name%}"echo Created zip file: %zip_name% +rem Rename the temporary zip to the desired name +ren "%zip_name%" "skp2ocli.rbz" + +echo renamed zip file: skp2ocli.rbz + +pause +``` + +*NB*: The output file will be saved in the directory where the script was executed + +## Installation +1. You must click on Extension in the menu +![first step](./img/installation-1.png) +2. From the dropdown menu, click Extension Manager +![second step](./img/installation-2.png) +3. From the new windows that pops up, click on Install Extension +![third step](./img/installation-3.png) + + + +## Remove +To remove the plugin, simply follow the steps below +1. You must click on Extension in the menu +![first step](./img/installation-1.png) +2. From the dropdown menu, click Extension Manager +![second step](./img/installation-2.png) +3. Click on Manage to change tab +![third step](./img/remove-3.png) + +## Code details? +To get details about the code you can check directly the source code which is well documented and you can also check directory **[html page](./doc/index.html)** which contains a webpage that document the different functions and classes implemented. diff --git a/skp2ocli/create_rbz.bat b/skp2ocli/create_rbz.bat new file mode 100644 index 0000000..4c3d9d2 --- /dev/null +++ b/skp2ocli/create_rbz.bat @@ -0,0 +1,24 @@ +@echo off + +rem Define the directory to zip (replace "mydirectory" with your actual directory) +set "dir_to_zip=skp2ocli" + +rem Define the file to include (replace "myfile.txt" with your actual filename) +set "file_to_include=skp2ocli.rb" + +rem Get the current directory (where the script is executed) +set "script_dir=%~dp0" + +rem Build the zip filename based on directory name and timestamp +set "zip_name=skp2ocli.zip" +if exist "skp2ocli.rbz" ( + del /q "skp2ocli.rbz" + echo Deleted existing zip file: skp2ocli.rbz +) +PowerShell -ExecutionPolicy Bypass -NoProfile -Command "& { Compress-Archive -Path %dir_to_zip%* -DestinationPath %zip_name%}"echo Created zip file: %zip_name% +rem Rename the temporary zip to the desired name +ren "%zip_name%" "skp2ocli.rbz" + +echo renamed zip file: skp2ocli.rbz + +pause \ No newline at end of file diff --git a/skp2ocli/doc/Skp2ocli.html b/skp2ocli/doc/Skp2ocli.html new file mode 100644 index 0000000..545a60a --- /dev/null +++ b/skp2ocli/doc/Skp2ocli.html @@ -0,0 +1,91 @@ + + + + + + +module Skp2ocli - RDoc Documentation + + + + + + + + + + + + + + + + +
+

+ module Skp2ocli +

+ +
+ +
+ +
+ + + + + +
+
+ + + + diff --git a/skp2ocli/doc/Skp2ocli/OCliExport.html b/skp2ocli/doc/Skp2ocli/OCliExport.html new file mode 100644 index 0000000..2dd41a1 --- /dev/null +++ b/skp2ocli/doc/Skp2ocli/OCliExport.html @@ -0,0 +1,580 @@ + + + + + + +module Skp2ocli::OCliExport - RDoc Documentation + + + + + + + + + + + + + + + + +
+

+ module Skp2ocli::OCliExport +

+ +
+ +
+ +
+ + +
+
+

Constants

+
+
+
TOLERANCE +
+
+
+ + + +
+
+

Public Class Methods

+
+ +
+
+
+ angle_between_vectors(vector1, vector2) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 38
+def self.angle_between_vectors(vector1, vector2)
+  dot_product = vector1.dot(vector2)
+  magnitudes = vector1.length * vector2.length
+  Math.acos(dot_product / magnitudes).radians
+end
+
+
+ + +
+ +
+
+
+ calculate_total_edge_length(entity) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 64
+def self.calculate_total_edge_length(entity)
+  case entity
+  when Sketchup::Group, Sketchup::ComponentInstance
+    entity.definition.entities.grep(Sketchup::Edge).map { |e| e.length.to_i }
+  when Sketchup::Face
+    entity.edges.map { |edge| edge.length.to_i }
+  when Sketchup::Edge
+    [entity.length.to_i]
+  when Sketchup::Entities
+    entity.grep(Sketchup::Edge).map { |e| e.length.to_i }
+  else
+    []
+  end
+end
+
+
+ + +
+ +
+
+
+ check_intersection(selected_entity, all_entities) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 83
+def self.check_intersection(selected_entity, all_entities)
+  all_entities.reject { |entity| entity == selected_entity || entity.parent == selected_entity }
+              .select { |entity| entities_intersect?(selected_entity, entity) }
+              .uniq
+end
+
+
+ + +
+ +
+
+
+ check_right_angles(entity) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 8
+def self.check_right_angles(entity)
+  face = get_face(entity)
+  return nil unless face.is_a?(Sketchup::Face)
+  return nil unless face.edges.length == 4
+
+  vertices = face.vertices
+  return nil unless vertices.length == 4
+
+  vectors = vertices.each_with_index.map do |vertex, i|
+    next_vertex = vertices[(i + 1) % 4]
+    vertex.position.vector_to(next_vertex.position)
+  end
+
+  angles = vectors.each_with_index.map do |vector, i|
+    next_vector = vectors[(i + 1) % 4]
+    angle_between_vectors(vector, next_vector)
+  end
+
+  angles.all? { |angle| (90 - angle).abs < TOLERANCE }
+end
+
+
+ + +
+ +
+
+
+ entities_intersect?(entity1, entity2) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 79
+def self.entities_intersect?(entity1, entity2)
+  entity1.bounds.intersect(entity2.bounds).valid?
+end
+
+
+ + +
+ +
+
+
+ export(data, path) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 185
+def self.export(data, path)
+  File.open(path, "w") do |file|
+    # Write the JSON data to the file
+    file.write(JSON.pretty_generate(data))
+    puts "Data exported successfully to #{path}"
+  end
+end
+
+
+ + +
+ +
+
+
+ extract_children_entities(entity) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 141
+def self.extract_children_entities(entity)
+  # return empty list if no children
+  definition = entity.definition
+  if not definition.entities
+    return []
+  else
+    entities_details = []
+    definition.entities.each do |child_entity|
+      # Get details of each child entity
+      entities_details << get_entity_details(child_entity)
+    end
+    return entities_details
+  end
+end
+
+
+ + +
+ +
+
+
+ extract_model_objects(model) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 156
+def self.extract_model_objects(model)
+  # Initialize empty array to store component data
+  objects = []
+  model.entities.each do |entity|
+    # Check if entity is a component instance
+    next unless entity.is_a?(Sketchup::ComponentInstance) or entity.is_a?(Sketchup::Group)
+
+    # Get the component definition
+    definition = entity.definition
+
+    # Collect component details
+    component_data = {
+      "name": definition.name,
+      "description": definition.description,
+      "exact_position": entity.transformation.origin,  # Get transformation for component instance
+    }
+    component_data["position"] =  {
+      "x":entity.transformation.origin.x, 
+      "y":entity.transformation.origin.y, 
+      "z":entity.transformation.origin.z
+    }
+    # get children entities
+      component_data["entities"] = extract_children_entities(entity)
+    # Store component data
+    objects << component_data
+  end
+  return objects
+end
+
+
+ + +
+ +
+
+
+ face_and_adjacent_are_squares?(face) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 58
+def self.face_and_adjacent_are_squares?(face)
+  return false unless is_square?(face)
+  adjacent_faces = face.edges.flat_map(&:faces).uniq - [face]
+  adjacent_faces.all? { |adjacent_face| is_square?(adjacent_face) }
+end
+
+
+ + +
+ +
+
+
+ get_entity_coordinates(entity, transform) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 89
+def self.get_entity_coordinates(entity, transform)
+  origin = Geom::Point3d.new(0, 0, 0)
+  local_origin = origin.transform(transform)
+  global_origin = entity.transformation * origin
+  {
+    local: local_origin.to_a.map(&:to_i),
+    global: global_origin.to_a.map(&:to_i)
+  }
+end
+
+
+ + +
+ +
+
+
+ get_entity_details(child_entity) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 105
+def self.get_entity_details(child_entity)
+  details = {
+    "type": child_entity.typename,
+  }
+  case child_entity
+    when Sketchup::Face
+      details["points"] = child_entity.vertices.map(&:position).map(&:to_a)  # Get vertex positions as arrays
+      details["material"] = child_entity.material ? child_entity.material.name : nil  # Material name (if available)
+    when Sketchup::Edge
+      details["start_point"] = {
+        "x": child_entity.start.position.x,
+        "y":child_entity.start.position.y,
+        "z":child_entity.start.position.z, 
+      } # Start point position
+      details["end_point"] = {
+        "x": child_entity.end.position.x,
+        "y":child_entity.end.position.y,
+        "z":child_entity.end.position.z, 
+      }    # End point position
+      details["length"] = child_entity.length
+    # Add cases for other geometry types (arc, solid, etc.) with their specific details
+    when Sketchup::Group
+      details["material"] = child_entity.material ? child_entity.material.name : nil  # Material name (if available)
+      entities_details = []
+      child_entity.definition.entities.each do |child_entity|
+        # Get details of each child entity
+        # here error below
+        child_details = get_entity_details(child_entity)
+        # Get the component's origin (outside the loop)
+        entities_details << child_details
+      end
+      details["entities"] = entities_details
+  end
+  return details
+end
+
+
+ + +
+ +
+
+
+ get_entity_identifier(entity) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 99
+def self.get_entity_identifier(entity)
+  entity_tag = entity.layer.display_name
+  entity_definition = entity.definition.name if entity.respond_to?(:definition)
+  "Definition: #{entity_definition}, Tag: #{entity_tag}"
+end
+
+
+ + +
+ +
+
+
+ get_face(entity) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 29
+def self.get_face(entity)
+  case entity
+  when Sketchup::Face then entity
+  when Sketchup::Edge then entity.faces.first
+  when Sketchup::Vertex then entity.edges.map(&:faces).flatten.uniq.first
+  else nil
+  end
+end
+
+
+ + +
+ +
+
+
+ is_square?(face) + click to toggle source +
+
+ +
+ + +
+
# File main.rb, line 44
+def self.is_square?(face)
+  return false unless face.is_a?(Sketchup::Face) && face.edges.length == 4
+  vertices = face.vertices
+  return false unless vertices.length == 4
+
+  vectors = vertices.each_with_index.map do |vertex, i|
+    next_vertex = vertices[(i + 1) % 4]
+    vertex.position.vector_to(next_vertex.position)
+  end
+
+  lengths = vectors.map(&:length)
+  lengths.uniq.length == 1
+end
+
+
+ + +
+ +
+ +
+
+ + + + diff --git a/skp2ocli/doc/created.rid b/skp2ocli/doc/created.rid new file mode 100644 index 0000000..6f62537 --- /dev/null +++ b/skp2ocli/doc/created.rid @@ -0,0 +1,2 @@ +Fri, 19 Jul 2024 15:08:51 +0200 +main.rb Fri, 19 Jul 2024 15:08:41 +0200 diff --git a/skp2ocli/doc/css/fonts.css b/skp2ocli/doc/css/fonts.css new file mode 100644 index 0000000..57302b5 --- /dev/null +++ b/skp2ocli/doc/css/fonts.css @@ -0,0 +1,167 @@ +/* + * Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + * with Reserved Font Name "Source". All Rights Reserved. Source is a + * trademark of Adobe Systems Incorporated in the United States and/or other + * countries. + * + * This Font Software is licensed under the SIL Open Font License, Version + * 1.1. + * + * This license is copied below, and is also available with a FAQ at: + * http://scripts.sil.org/OFL + */ + +@font-face { + font-family: "Source Code Pro"; + font-style: normal; + font-weight: 400; + src: local("Source Code Pro"), + local("SourceCodePro-Regular"), + url("../fonts/SourceCodePro-Regular.ttf") format("truetype"); +} + +@font-face { + font-family: "Source Code Pro"; + font-style: normal; + font-weight: 700; + src: local("Source Code Pro Bold"), + local("SourceCodePro-Bold"), + url("../fonts/SourceCodePro-Bold.ttf") format("truetype"); +} + +/* + * Copyright (c) 2010, Ɓukasz Dziedzic (dziedzic@typoland.com), + * with Reserved Font Name Lato. + * + * This Font Software is licensed under the SIL Open Font License, Version + * 1.1. + * + * This license is copied below, and is also available with a FAQ at: + * http://scripts.sil.org/OFL + */ + +@font-face { + font-family: "Lato"; + font-style: normal; + font-weight: 300; + src: local("Lato Light"), + local("Lato-Light"), + url("../fonts/Lato-Light.ttf") format("truetype"); +} + +@font-face { + font-family: "Lato"; + font-style: italic; + font-weight: 300; + src: local("Lato Light Italic"), + local("Lato-LightItalic"), + url("../fonts/Lato-LightItalic.ttf") format("truetype"); +} + +@font-face { + font-family: "Lato"; + font-style: normal; + font-weight: 700; + src: local("Lato Regular"), + local("Lato-Regular"), + url("../fonts/Lato-Regular.ttf") format("truetype"); +} + +@font-face { + font-family: "Lato"; + font-style: italic; + font-weight: 700; + src: local("Lato Italic"), + local("Lato-Italic"), + url("../fonts/Lato-RegularItalic.ttf") format("truetype"); +} + +/* + * ----------------------------------------------------------- + * SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 + * ----------------------------------------------------------- + * + * PREAMBLE + * The goals of the Open Font License (OFL) are to stimulate worldwide + * development of collaborative font projects, to support the font creation + * efforts of academic and linguistic communities, and to provide a free and + * open framework in which fonts may be shared and improved in partnership + * with others. + * + * The OFL allows the licensed fonts to be used, studied, modified and + * redistributed freely as long as they are not sold by themselves. The + * fonts, including any derivative works, can be bundled, embedded, + * redistributed and/or sold with any software provided that any reserved + * names are not used by derivative works. The fonts and derivatives, + * however, cannot be released under any other type of license. The + * requirement for fonts to remain under this license does not apply + * to any document created using the fonts or their derivatives. + * + * DEFINITIONS + * "Font Software" refers to the set of files released by the Copyright + * Holder(s) under this license and clearly marked as such. This may + * include source files, build scripts and documentation. + * + * "Reserved Font Name" refers to any names specified as such after the + * copyright statement(s). + * + * "Original Version" refers to the collection of Font Software components as + * distributed by the Copyright Holder(s). + * + * "Modified Version" refers to any derivative made by adding to, deleting, + * or substituting -- in part or in whole -- any of the components of the + * Original Version, by changing formats or by porting the Font Software to a + * new environment. + * + * "Author" refers to any designer, engineer, programmer, technical + * writer or other person who contributed to the Font Software. + * + * PERMISSION & CONDITIONS + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of the Font Software, to use, study, copy, merge, embed, modify, + * redistribute, and sell modified and unmodified copies of the Font + * Software, subject to the following conditions: + * + * 1) Neither the Font Software nor any of its individual components, + * in Original or Modified Versions, may be sold by itself. + * + * 2) Original or Modified Versions of the Font Software may be bundled, + * redistributed and/or sold with any software, provided that each copy + * contains the above copyright notice and this license. These can be + * included either as stand-alone text files, human-readable headers or + * in the appropriate machine-readable metadata fields within text or + * binary files as long as those fields can be easily viewed by the user. + * + * 3) No Modified Version of the Font Software may use the Reserved Font + * Name(s) unless explicit written permission is granted by the corresponding + * Copyright Holder. This restriction only applies to the primary font name as + * presented to the users. + * + * 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font + * Software shall not be used to promote, endorse or advertise any + * Modified Version, except to acknowledge the contribution(s) of the + * Copyright Holder(s) and the Author(s) or with their explicit written + * permission. + * + * 5) The Font Software, modified or unmodified, in part or in whole, + * must be distributed entirely under this license, and must not be + * distributed under any other license. The requirement for fonts to + * remain under this license does not apply to any document created + * using the Font Software. + * + * TERMINATION + * This license becomes null and void if any of the above conditions are + * not met. + * + * DISCLAIMER + * THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL + * DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM + * OTHER DEALINGS IN THE FONT SOFTWARE. + */ + diff --git a/skp2ocli/doc/css/rdoc.css b/skp2ocli/doc/css/rdoc.css new file mode 100644 index 0000000..1be815f --- /dev/null +++ b/skp2ocli/doc/css/rdoc.css @@ -0,0 +1,662 @@ +/* + * "Darkfish" Rdoc CSS + * $Id: rdoc.css 54 2009-01-27 01:09:48Z deveiant $ + * + * Author: Michael Granger + * + */ + +/* vim: ft=css et sw=2 ts=2 sts=2 */ +/* Base Green is: #6C8C22 */ + +.hide { display: none !important; } + +* { padding: 0; margin: 0; } + +body { + background: #fafafa; + font-family: Lato, sans-serif; + font-weight: 300; +} + +h1 span, +h2 span, +h3 span, +h4 span, +h5 span, +h6 span { + position: relative; + + display: none; + padding-left: 1em; + line-height: 0; + vertical-align: baseline; + font-size: 10px; +} + +h1 span { top: -1.3em; } +h2 span { top: -1.2em; } +h3 span { top: -1.0em; } +h4 span { top: -0.8em; } +h5 span { top: -0.5em; } +h6 span { top: -0.5em; } + +h1:hover span, +h2:hover span, +h3:hover span, +h4:hover span, +h5:hover span, +h6:hover span { + display: inline; +} + +h1:target, +h2:target, +h3:target, +h4:target, +h5:target, +h6:target { + margin-left: -10px; + border-left: 10px solid #f1edba; +} + +:link, +:visited { + color: #6C8C22; + text-decoration: none; +} + +:link:hover, +:visited:hover { + border-bottom: 1px dotted #6C8C22; +} + +code, +pre { + font-family: "Source Code Pro", Monaco, monospace; + background-color: rgba(27,31,35,0.05); + padding: 0em 0.2em; + border-radius: 0.2em; +} + +table { + margin: 0; + border-spacing: 0; + border-collapse: collapse; +} + +table tr th, table tr td { + padding: 0.2em 0.4em; + border: 1px solid #ccc; +} + +table tr th { + background-color: #eceaed; +} + +table tr:nth-child(even) td { + background-color: #f5f4f6; +} + +/* @group Generic Classes */ + +.initially-hidden { + display: none; +} + +#search-field { + width: 98%; + background: white; + border: none; + height: 1.5em; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + text-align: left; +} +#search-field:focus { + background: #f1edba; +} +#search-field:-moz-placeholder, +#search-field::-webkit-input-placeholder { + font-weight: bold; + color: #666; +} + +.missing-docs { + font-size: 120%; + background: white url(../images/wrench_orange.png) no-repeat 4px center; + color: #ccc; + line-height: 2em; + border: 1px solid #d00; + opacity: 1; + padding-left: 20px; + text-indent: 24px; + letter-spacing: 3px; + font-weight: bold; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; +} + +.target-section { + border: 2px solid #dcce90; + border-left-width: 8px; + padding: 0 1em; + background: #fff3c2; +} + +/* @end */ + +/* @group Index Page, Standalone file pages */ +.table-of-contents ul { + margin: 1em; + list-style: none; +} + +.table-of-contents ul ul { + margin-top: 0.25em; +} + +.table-of-contents ul :link, +.table-of-contents ul :visited { + font-size: 16px; +} + +.table-of-contents li { + margin-bottom: 0.25em; +} + +.table-of-contents li .toc-toggle { + width: 16px; + height: 16px; + background: url(../images/add.png) no-repeat; +} + +.table-of-contents li .toc-toggle.open { + background: url(../images/delete.png) no-repeat; +} + +/* @end */ + +/* @group Top-Level Structure */ + +nav { + float: left; + width: 260px; + font-family: Helvetica, sans-serif; + font-size: 14px; + border-right: 1px solid #ccc; + position: sticky; + top: 0; + overflow: auto; + height: calc(100vh - 100px); /* reduce the footer height */ +} + +main { + display: block; + margin: 0 2em 5em 260px; + padding-left: 20px; + min-width: 340px; + font-size: 16px; +} + +main h1, +main h2, +main h3, +main h4, +main h5, +main h6 { + font-family: Helvetica, sans-serif; +} + +.table-of-contents main { + margin-left: 2em; +} + +#validator-badges { + clear: both; + margin: 1em 1em 2em; + font-size: smaller; +} + +/* @end */ + +/* @group navigation */ +nav { + margin-bottom: 1em; +} + +nav .nav-section { + margin-top: 2em; + border-top: 2px solid #aaa; + font-size: 90%; + overflow: hidden; +} + +nav h2 { + margin: 0; + padding: 2px 8px 2px 8px; + background-color: #e8e8e8; + color: #555; + font-size: 125%; + text-align: center; +} + +nav h3, +#table-of-contents-navigation { + margin: 0; + padding: 2px 8px 2px 8px; + text-align: right; + background-color: #e8e8e8; + color: #555; +} + +nav ul, +nav dl, +nav p { + padding: 4px 8px 0; + list-style: none; +} + +#project-navigation .nav-section { + margin: 0; + border-top: 0; +} + +#home-section h2 { + text-align: center; +} + +#table-of-contents-navigation { + font-size: 1.2em; + font-weight: bold; + text-align: center; +} + +#search-section { + margin-top: 0; + border-top: 0; +} + +#search-field-wrapper { + border-top: 1px solid #aaa; + border-bottom: 1px solid #aaa; + padding: 3px 8px; + background-color: #e8e8e8; + color: #555; +} + +ul.link-list li { + white-space: nowrap; + line-height: 1.4em; +} + +ul.link-list .type { + font-size: 8px; + text-transform: uppercase; + color: white; + background: #969696; + padding: 2px 4px; + -webkit-border-radius: 5px; +} + +dl.note-list dt { + float: left; + margin-right: 1em; +} + +.calls-super { + background: url(../images/arrow_up.png) no-repeat right center; +} + +.nav-section details summary { + display: block; +} + +.nav-section details summary::-webkit-details-marker { + display: none; +} + +.nav-section details summary:before { + content: ""; +} + +.nav-section details summary:after { + content: " \25B6"; /* BLACK RIGHT-POINTING TRIANGLE */ +} +.nav-section details[open] > summary:after { + content: " \25BD"; /* WHITE DOWN-POINTING TRIANGLE */ +} + +/* @end */ + +/* @group Documentation Section */ +main { + color: #333; +} + +main > h1:first-child, +main > h2:first-child, +main > h3:first-child, +main > h4:first-child, +main > h5:first-child, +main > h6:first-child { + margin-top: 0px; +} + +main sup { + vertical-align: super; + font-size: 0.8em; +} + +/* The heading with the class name */ +main h1[class] { + margin-top: 0; + margin-bottom: 1em; + font-size: 2em; + color: #6C8C22; +} + +main h1 { + margin: 2em 0 0.5em; + font-size: 1.7em; +} + +main h2 { + margin: 2em 0 0.5em; + font-size: 1.5em; +} + +main h3 { + margin: 2em 0 0.5em; + font-size: 1.2em; +} + +main h4 { + margin: 2em 0 0.5em; + font-size: 1.1em; +} + +main h5 { + margin: 2em 0 0.5em; + font-size: 1em; +} + +main h6 { + margin: 2em 0 0.5em; + font-size: 1em; +} + +main p { + margin: 0 0 0.5em; + line-height: 1.4em; +} + +main pre { + margin: 1.2em 0.5em; + padding: 1em; + font-size: 0.8em; +} + +main hr { + margin: 1.5em 1em; + border: 2px solid #ddd; +} + +main blockquote { + margin: 0 2em 1.2em 1.2em; + padding-left: 0.5em; + border-left: 2px solid #ddd; +} + +main ol, +main ul { + margin: 1em 2em; +} + +main li > p { + margin-bottom: 0.5em; +} + +main dl { + margin: 1em 0.5em; +} + +main dt { + margin-bottom: 0.5em; + font-weight: bold; +} + +main dd { + margin: 0 1em 1em 0.5em; +} + +main header h2 { + margin-top: 2em; + border-width: 0; + border-top: 4px solid #bbb; + font-size: 130%; +} + +main header h3 { + margin: 2em 0 1.5em; + border-width: 0; + border-top: 3px solid #bbb; + font-size: 120%; +} + +.documentation-section-title { + position: relative; +} +.documentation-section-title .section-click-top { + position: absolute; + top: 6px; + left: 12px; + font-size: 10px; + color: #9b9877; + visibility: hidden; + padding-left: 0.5px; +} + +.documentation-section-title:hover .section-click-top { + visibility: visible; +} + +.constants-list > dl { + margin: 1em 0 2em; + border: 0; +} + +.constants-list > dl dt { + margin-bottom: 0.75em; + padding-left: 0; + font-family: "Source Code Pro", Monaco, monospace; + font-size: 110%; +} + +.constants-list > dl dt a { + color: inherit; +} + +.constants-list > dl dd { + margin: 0 0 2em 0; + padding: 0; + color: #666; +} + +.documentation-section h2 { + position: relative; +} + +.documentation-section h2 a { + position: absolute; + top: 8px; + right: 10px; + font-size: 12px; + color: #9b9877; + visibility: hidden; +} + +.documentation-section h2:hover a { + visibility: visible; +} + +/* @group Method Details */ + +main .method-source-code { + max-height: 0; + overflow: auto; + transition-duration: 200ms; + transition-delay: 0ms; + transition-property: all; + transition-timing-function: ease-in-out; +} + +main .method-source-code.active-menu { + max-height: 100vh; +} + +main .method-description .method-calls-super { + color: #333; + font-weight: bold; +} + +main .method-detail { + margin-bottom: 2.5em; + cursor: pointer; +} + +main .method-detail:target { + margin-left: -10px; + border-left: 10px solid #f1edba; +} + +main .method-heading { + position: relative; + font-family: "Source Code Pro", Monaco, monospace; + font-size: 110%; + font-weight: bold; + color: #333; +} +main .method-heading :link, +main .method-heading :visited { + color: inherit; +} +main .method-click-advice { + position: absolute; + top: 2px; + right: 5px; + font-size: 12px; + color: #9b9877; + visibility: hidden; + padding-right: 20px; + line-height: 20px; + background: url(../images/zoom.png) no-repeat right top; +} +main .method-heading:hover .method-click-advice { + visibility: visible; +} + +main .method-alias .method-heading { + color: #666; +} + +main .method-description, +main .aliases { + margin-top: 0.75em; + color: #333; +} + +main .aliases { + padding-top: 4px; + font-style: italic; + cursor: default; +} +main .method-description ul { + margin-left: 1.5em; +} + +main #attribute-method-details .method-detail:hover { + background-color: transparent; + cursor: default; +} +main .attribute-access-type { + text-transform: uppercase; + padding: 0 1em; +} +/* @end */ + +/* @end */ + +/* @group Source Code */ + +pre { + margin: 0.5em 0; + border: 1px dashed #999; + padding: 0.5em; + background: #262626; + color: white; + overflow: auto; +} + +.ruby-constant { color: #7fffd4; background: transparent; } +.ruby-keyword { color: #00ffff; background: transparent; } +.ruby-ivar { color: #eedd82; background: transparent; } +.ruby-operator { color: #00ffee; background: transparent; } +.ruby-identifier { color: #ffdead; background: transparent; } +.ruby-node { color: #ffa07a; background: transparent; } +.ruby-comment { color: #dc0000; background: transparent; } +.ruby-regexp { color: #ffa07a; background: transparent; } +.ruby-value { color: #7fffd4; background: transparent; } + +/* @end */ + + +/* @group search results */ +#search-results { + font-family: Lato, sans-serif; + font-weight: 300; +} + +#search-results .search-match { + font-family: Helvetica, sans-serif; + font-weight: normal; +} + +#search-results .search-selected { + background: #e8e8e8; + border-bottom: 1px solid transparent; +} + +#search-results li { + list-style: none; + border-bottom: 1px solid #aaa; + margin-bottom: 0.5em; +} + +#search-results li:last-child { + border-bottom: none; + margin-bottom: 0; +} + +#search-results li p { + padding: 0; + margin: 0.5em; +} + +#search-results .search-namespace { + font-weight: bold; +} + +#search-results li em { + background: yellow; + font-style: normal; +} + +#search-results pre { + margin: 0.5em; + font-family: "Source Code Pro", Monaco, monospace; +} + +/* @end */ + diff --git a/skp2ocli/doc/fonts/Lato-Light.ttf b/skp2ocli/doc/fonts/Lato-Light.ttf new file mode 100644 index 0000000..b49dd43 Binary files /dev/null and b/skp2ocli/doc/fonts/Lato-Light.ttf differ diff --git a/skp2ocli/doc/fonts/Lato-LightItalic.ttf b/skp2ocli/doc/fonts/Lato-LightItalic.ttf new file mode 100644 index 0000000..7959fef Binary files /dev/null and b/skp2ocli/doc/fonts/Lato-LightItalic.ttf differ diff --git a/skp2ocli/doc/fonts/Lato-Regular.ttf b/skp2ocli/doc/fonts/Lato-Regular.ttf new file mode 100644 index 0000000..839cd58 Binary files /dev/null and b/skp2ocli/doc/fonts/Lato-Regular.ttf differ diff --git a/skp2ocli/doc/fonts/Lato-RegularItalic.ttf b/skp2ocli/doc/fonts/Lato-RegularItalic.ttf new file mode 100644 index 0000000..bababa0 Binary files /dev/null and b/skp2ocli/doc/fonts/Lato-RegularItalic.ttf differ diff --git a/skp2ocli/doc/fonts/SourceCodePro-Bold.ttf b/skp2ocli/doc/fonts/SourceCodePro-Bold.ttf new file mode 100644 index 0000000..dd00982 Binary files /dev/null and b/skp2ocli/doc/fonts/SourceCodePro-Bold.ttf differ diff --git a/skp2ocli/doc/fonts/SourceCodePro-Regular.ttf b/skp2ocli/doc/fonts/SourceCodePro-Regular.ttf new file mode 100644 index 0000000..1decfb9 Binary files /dev/null and b/skp2ocli/doc/fonts/SourceCodePro-Regular.ttf differ diff --git a/skp2ocli/doc/images/add.png b/skp2ocli/doc/images/add.png new file mode 100644 index 0000000..6332fef Binary files /dev/null and b/skp2ocli/doc/images/add.png differ diff --git a/skp2ocli/doc/images/arrow_up.png b/skp2ocli/doc/images/arrow_up.png new file mode 100644 index 0000000..1ebb193 Binary files /dev/null and b/skp2ocli/doc/images/arrow_up.png differ diff --git a/skp2ocli/doc/images/brick.png b/skp2ocli/doc/images/brick.png new file mode 100644 index 0000000..7851cf3 Binary files /dev/null and b/skp2ocli/doc/images/brick.png differ diff --git a/skp2ocli/doc/images/brick_link.png b/skp2ocli/doc/images/brick_link.png new file mode 100644 index 0000000..9ebf013 Binary files /dev/null and b/skp2ocli/doc/images/brick_link.png differ diff --git a/skp2ocli/doc/images/bug.png b/skp2ocli/doc/images/bug.png new file mode 100644 index 0000000..2d5fb90 Binary files /dev/null and b/skp2ocli/doc/images/bug.png differ diff --git a/skp2ocli/doc/images/bullet_black.png b/skp2ocli/doc/images/bullet_black.png new file mode 100644 index 0000000..5761970 Binary files /dev/null and b/skp2ocli/doc/images/bullet_black.png differ diff --git a/skp2ocli/doc/images/bullet_toggle_minus.png b/skp2ocli/doc/images/bullet_toggle_minus.png new file mode 100644 index 0000000..b47ce55 Binary files /dev/null and b/skp2ocli/doc/images/bullet_toggle_minus.png differ diff --git a/skp2ocli/doc/images/bullet_toggle_plus.png b/skp2ocli/doc/images/bullet_toggle_plus.png new file mode 100644 index 0000000..9ab4a89 Binary files /dev/null and b/skp2ocli/doc/images/bullet_toggle_plus.png differ diff --git a/skp2ocli/doc/images/date.png b/skp2ocli/doc/images/date.png new file mode 100644 index 0000000..783c833 Binary files /dev/null and b/skp2ocli/doc/images/date.png differ diff --git a/skp2ocli/doc/images/delete.png b/skp2ocli/doc/images/delete.png new file mode 100644 index 0000000..08f2493 Binary files /dev/null and b/skp2ocli/doc/images/delete.png differ diff --git a/skp2ocli/doc/images/find.png b/skp2ocli/doc/images/find.png new file mode 100644 index 0000000..1547479 Binary files /dev/null and b/skp2ocli/doc/images/find.png differ diff --git a/skp2ocli/doc/images/loadingAnimation.gif b/skp2ocli/doc/images/loadingAnimation.gif new file mode 100644 index 0000000..82290f4 Binary files /dev/null and b/skp2ocli/doc/images/loadingAnimation.gif differ diff --git a/skp2ocli/doc/images/macFFBgHack.png b/skp2ocli/doc/images/macFFBgHack.png new file mode 100644 index 0000000..c6473b3 Binary files /dev/null and b/skp2ocli/doc/images/macFFBgHack.png differ diff --git a/skp2ocli/doc/images/package.png b/skp2ocli/doc/images/package.png new file mode 100644 index 0000000..da3c2a2 Binary files /dev/null and b/skp2ocli/doc/images/package.png differ diff --git a/skp2ocli/doc/images/page_green.png b/skp2ocli/doc/images/page_green.png new file mode 100644 index 0000000..de8e003 Binary files /dev/null and b/skp2ocli/doc/images/page_green.png differ diff --git a/skp2ocli/doc/images/page_white_text.png b/skp2ocli/doc/images/page_white_text.png new file mode 100644 index 0000000..813f712 Binary files /dev/null and b/skp2ocli/doc/images/page_white_text.png differ diff --git a/skp2ocli/doc/images/page_white_width.png b/skp2ocli/doc/images/page_white_width.png new file mode 100644 index 0000000..1eb8809 Binary files /dev/null and b/skp2ocli/doc/images/page_white_width.png differ diff --git a/skp2ocli/doc/images/plugin.png b/skp2ocli/doc/images/plugin.png new file mode 100644 index 0000000..6187b15 Binary files /dev/null and b/skp2ocli/doc/images/plugin.png differ diff --git a/skp2ocli/doc/images/ruby.png b/skp2ocli/doc/images/ruby.png new file mode 100644 index 0000000..f763a16 Binary files /dev/null and b/skp2ocli/doc/images/ruby.png differ diff --git a/skp2ocli/doc/images/tag_blue.png b/skp2ocli/doc/images/tag_blue.png new file mode 100644 index 0000000..3f02b5f Binary files /dev/null and b/skp2ocli/doc/images/tag_blue.png differ diff --git a/skp2ocli/doc/images/tag_green.png b/skp2ocli/doc/images/tag_green.png new file mode 100644 index 0000000..83ec984 Binary files /dev/null and b/skp2ocli/doc/images/tag_green.png differ diff --git a/skp2ocli/doc/images/transparent.png b/skp2ocli/doc/images/transparent.png new file mode 100644 index 0000000..d665e17 Binary files /dev/null and b/skp2ocli/doc/images/transparent.png differ diff --git a/skp2ocli/doc/images/wrench.png b/skp2ocli/doc/images/wrench.png new file mode 100644 index 0000000..5c8213f Binary files /dev/null and b/skp2ocli/doc/images/wrench.png differ diff --git a/skp2ocli/doc/images/wrench_orange.png b/skp2ocli/doc/images/wrench_orange.png new file mode 100644 index 0000000..565a933 Binary files /dev/null and b/skp2ocli/doc/images/wrench_orange.png differ diff --git a/skp2ocli/doc/images/zoom.png b/skp2ocli/doc/images/zoom.png new file mode 100644 index 0000000..908612e Binary files /dev/null and b/skp2ocli/doc/images/zoom.png differ diff --git a/skp2ocli/doc/index.html b/skp2ocli/doc/index.html new file mode 100644 index 0000000..a675df9 --- /dev/null +++ b/skp2ocli/doc/index.html @@ -0,0 +1,83 @@ + + + + + + +RDoc Documentation + + + + + + + + + + + + + + + + +
+

This is the API documentation for RDoc Documentation. +

+ + + + diff --git a/skp2ocli/doc/js/darkfish.js b/skp2ocli/doc/js/darkfish.js new file mode 100644 index 0000000..d0c9467 --- /dev/null +++ b/skp2ocli/doc/js/darkfish.js @@ -0,0 +1,84 @@ +/** + * + * Darkfish Page Functions + * $Id: darkfish.js 53 2009-01-07 02:52:03Z deveiant $ + * + * Author: Michael Granger + * + */ + +/* Provide console simulation for firebug-less environments */ +/* +if (!("console" in window) || !("firebug" in console)) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", + "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +}; +*/ + + +function showSource( e ) { + var target = e.target; + while (!target.classList.contains('method-detail')) { + target = target.parentNode; + } + if (typeof target !== "undefined" && target !== null) { + target = target.querySelector('.method-source-code'); + } + if (typeof target !== "undefined" && target !== null) { + target.classList.toggle('active-menu') + } +}; + +function hookSourceViews() { + document.querySelectorAll('.method-heading').forEach(function (codeObject) { + codeObject.addEventListener('click', showSource); + }); +}; + +function hookSearch() { + var input = document.querySelector('#search-field'); + var result = document.querySelector('#search-results'); + result.classList.remove("initially-hidden"); + + var search_section = document.querySelector('#search-section'); + search_section.classList.remove("initially-hidden"); + + var search = new Search(search_data, input, result); + + search.renderItem = function(result) { + var li = document.createElement('li'); + var html = ''; + + // TODO add relative path to + + + + + + + + + + + + +
+

Table of Contents - RDoc Documentation

+ + +

Classes and Modules

+ + +

Methods

+ +
+ + + + diff --git a/skp2ocli/img/installation-1.png b/skp2ocli/img/installation-1.png new file mode 100644 index 0000000..d8386a5 Binary files /dev/null and b/skp2ocli/img/installation-1.png differ diff --git a/skp2ocli/img/installation-2.png b/skp2ocli/img/installation-2.png new file mode 100644 index 0000000..2889000 Binary files /dev/null and b/skp2ocli/img/installation-2.png differ diff --git a/skp2ocli/img/installation-3.png b/skp2ocli/img/installation-3.png new file mode 100644 index 0000000..13565e7 Binary files /dev/null and b/skp2ocli/img/installation-3.png differ diff --git a/skp2ocli/img/remove-3.png b/skp2ocli/img/remove-3.png new file mode 100644 index 0000000..ed136dd Binary files /dev/null and b/skp2ocli/img/remove-3.png differ diff --git a/skp2ocli/img/remove-4.png b/skp2ocli/img/remove-4.png new file mode 100644 index 0000000..01837cd Binary files /dev/null and b/skp2ocli/img/remove-4.png differ diff --git a/skp2ocli/img/usage-1.png b/skp2ocli/img/usage-1.png new file mode 100644 index 0000000..0a0d092 Binary files /dev/null and b/skp2ocli/img/usage-1.png differ diff --git a/skp2ocli/skp2ocli.rb b/skp2ocli/skp2ocli.rb new file mode 100644 index 0000000..ed99335 --- /dev/null +++ b/skp2ocli/skp2ocli.rb @@ -0,0 +1,35 @@ +# This Plugin will automatically generate a JSON file containing the +# details about the components of the Sketchup Model +# +# * sketchup.rb is needed for `file_loaded?` and `file_loaded`. +# +# * extensions.rb is needed for the `SketchupExtension` class. + +require "sketchup.rb" +require "extensions.rb" + +module Skp2ocli + module OCliExport + # The use of `file_loaded?` here is to prevent the extension from being + # registered multiple times. This can happen for a number of reasons when + # the file is reloaded - either when debugging during development or + # extension updates etc. + # + # The `__FILE__` constant is a "magic" Ruby constant that returns a string + # with the path to the current file. You don't have to use this constant + # with `file_loaded?` - you can use any unique string to represent this + # file. But `__FILE__` is very convenient for this. + unless file_loaded?(__FILE__) + ex = SketchupExtension.new('Generate OCLI files', 'skp2ocli/main') + ex.description = "Generate OCLI file that works with OGREE from Sketchup model." + ex.version = "0.0.1" + ex.copyright = "#{ex.creator} 2024" + ex.creator = "Kais BETTAIEB" + Sketchup.register_extension(ex, true) + + # This is needed for the load guard to prevent the extension being + # registered multiple times. + file_loaded(__FILE__) + end + end #end OCliExport +end # end Skp2ocli \ No newline at end of file diff --git a/skp2ocli/skp2ocli.rbz b/skp2ocli/skp2ocli.rbz new file mode 100644 index 0000000..ee958d8 Binary files /dev/null and b/skp2ocli/skp2ocli.rbz differ diff --git a/skp2ocli/skp2ocli/main.rb b/skp2ocli/skp2ocli/main.rb new file mode 100644 index 0000000..d0cd10d --- /dev/null +++ b/skp2ocli/skp2ocli/main.rb @@ -0,0 +1,272 @@ +require 'sketchup.rb' +require 'extensions.rb' + +module Skp2ocli + module OCliExport + TOLERANCE = 1.0 # Define TOLERANCE constant + + def self.check_right_angles(entity) + face = get_face(entity) + return nil unless face.is_a?(Sketchup::Face) + return nil unless face.edges.length == 4 + + vertices = face.vertices + return nil unless vertices.length == 4 + + vectors = vertices.each_with_index.map do |vertex, i| + next_vertex = vertices[(i + 1) % 4] + vertex.position.vector_to(next_vertex.position) + end + + angles = vectors.each_with_index.map do |vector, i| + next_vector = vectors[(i + 1) % 4] + angle_between_vectors(vector, next_vector) + end + + angles.all? { |angle| (90 - angle).abs < TOLERANCE } + end + + def self.get_face(entity) + case entity + when Sketchup::Face then entity + when Sketchup::Edge then entity.faces.first + when Sketchup::Vertex then entity.edges.map(&:faces).flatten.uniq.first + else nil + end + end + + def self.angle_between_vectors(vector1, vector2) + dot_product = vector1.dot(vector2) + magnitudes = vector1.length * vector2.length + Math.acos(dot_product / magnitudes).radians + end + + def self.is_square?(face) + return false unless face.is_a?(Sketchup::Face) && face.edges.length == 4 + vertices = face.vertices + return false unless vertices.length == 4 + + vectors = vertices.each_with_index.map do |vertex, i| + next_vertex = vertices[(i + 1) % 4] + vertex.position.vector_to(next_vertex.position) + end + + lengths = vectors.map(&:length) + lengths.uniq.length == 1 + end + + def self.face_and_adjacent_are_squares?(face) + return false unless is_square?(face) + adjacent_faces = face.edges.flat_map(&:faces).uniq - [face] + adjacent_faces.all? { |adjacent_face| is_square?(adjacent_face) } + end + + def self.calculate_total_edge_length(entity) + case entity + when Sketchup::Group, Sketchup::ComponentInstance + entity.definition.entities.grep(Sketchup::Edge).map { |e| e.length.to_i } + when Sketchup::Face + entity.edges.map { |edge| edge.length.to_i } + when Sketchup::Edge + [entity.length.to_i] + when Sketchup::Entities + entity.grep(Sketchup::Edge).map { |e| e.length.to_i } + else + [] + end + end + + def self.entities_intersect?(entity1, entity2) + entity1.bounds.intersect(entity2.bounds).valid? + end + + def self.check_intersection(selected_entity, all_entities) + all_entities.reject { |entity| entity == selected_entity || entity.parent == selected_entity } + .select { |entity| entities_intersect?(selected_entity, entity) } + .uniq + end + + def self.get_entity_coordinates(entity, transform) + origin = Geom::Point3d.new(0, 0, 0) + local_origin = origin.transform(transform) + global_origin = entity.transformation * origin + { + local: local_origin.to_a.map(&:to_i), + global: global_origin.to_a.map(&:to_i) + } + end + + def self.get_entity_identifier(entity) + entity_tag = entity.layer.display_name + entity_definition = entity.definition.name if entity.respond_to?(:definition) + "Definition: #{entity_definition}, Tag: #{entity_tag}" + end + + def self.get_entity_details(child_entity) + details = { + "type": child_entity.typename, + } + case child_entity + when Sketchup::Face + details["points"] = child_entity.vertices.map(&:position).map(&:to_a) # Get vertex positions as arrays + details["material"] = child_entity.material ? child_entity.material.name : nil # Material name (if available) + when Sketchup::Edge + details["start_point"] = { + "x": child_entity.start.position.x, + "y":child_entity.start.position.y, + "z":child_entity.start.position.z, + } # Start point position + details["end_point"] = { + "x": child_entity.end.position.x, + "y":child_entity.end.position.y, + "z":child_entity.end.position.z, + } # End point position + details["length"] = child_entity.length + # Add cases for other geometry types (arc, solid, etc.) with their specific details + when Sketchup::Group + details["material"] = child_entity.material ? child_entity.material.name : nil # Material name (if available) + entities_details = [] + child_entity.definition.entities.each do |child_entity| + # Get details of each child entity + # here error below + child_details = get_entity_details(child_entity) + # Get the component's origin (outside the loop) + entities_details << child_details + end + details["entities"] = entities_details + end + return details + end + + def self.extract_children_entities(entity) + # return empty list if no children + definition = entity.definition + if not definition.entities + return [] + else + entities_details = [] + definition.entities.each do |child_entity| + # Get details of each child entity + entities_details << get_entity_details(child_entity) + end + return entities_details + end + end + + def self.extract_model_objects(model) + # Initialize empty array to store component data + objects = [] + model.entities.each do |entity| + # Check if entity is a component instance + next unless entity.is_a?(Sketchup::ComponentInstance) or entity.is_a?(Sketchup::Group) + + # Get the component definition + definition = entity.definition + + # Collect component details + component_data = { + "name": definition.name, + "description": definition.description, + "exact_position": entity.transformation.origin, # Get transformation for component instance + } + component_data["position"] = { + "x":entity.transformation.origin.x, + "y":entity.transformation.origin.y, + "z":entity.transformation.origin.z + } + # get children entities + component_data["entities"] = extract_children_entities(entity) + # Store component data + objects << component_data + end + return objects + end + + def self.export(data, path) + File.open(path, "w") do |file| + # Write the JSON data to the file + file.write(JSON.pretty_generate(data)) + puts "Data exported successfully to #{path}" + end + end + + unless file_loaded?(__FILE__) + menu = UI.menu('Plugins') + menu.add_item('Generate OCLI files') { + model = Sketchup.active_model + selection = model.selection + entities = model.entities + + if selection.empty? + UI.messagebox("Please select a single face!") + return + end + + selected_entity = selection.first + angles = check_right_angles(selected_entity) + + if angles.nil? + puts "Failed to check object 3D shape" + else + total_length = calculate_total_edge_length(selected_entity) + puts "Total length of all edges: #{total_length}" + + if angles && total_length.uniq.size == 1 + puts "All edges of the selected entity are equal and all angles are 90 degrees. A cube" + #UI.messagebox("Cube") + elsif angles && total_length.uniq.size != 1 + puts "Edges of the selected entity are not all equal but all angles are 90 degrees. A cuboid" + #UI.messagebox("Cuboid") + else + puts "Edges of the selected entity are not all equal and all the angles are not 90 degrees" + #UI.messagebox("Unknown") + end + end + + parent_component = selected_entity + while parent_component.parent.is_a?(Sketchup::Entities) + parent_component = parent_component.parent + end + + unless parent_component.is_a?(Sketchup::ComponentInstance) || parent_component.is_a?(Sketchup::Group) + UI.messagebox("Please select an entity within a component or group.") + return + end + + selected_entity_identifier = get_entity_identifier(parent_component) + + all_entities = entities.grep(Sketchup::Group) + entities.grep(Sketchup::ComponentInstance) + intersecting_entities = check_intersection(parent_component, all_entities) + + if intersecting_entities.empty? + puts "No intersections found with the selected entity." + else + puts "##################" + intersecting_entities.each do |entity| + intersecting_entity_identifier = get_entity_identifier(entity) + + transform = entity.transformation.inverse * parent_component.transformation + + coords = get_entity_coordinates(parent_component, transform) + + puts "Selected entity (#{selected_entity_identifier}):" + puts " Global coordinates (origin 0,0,0): #{coords[:global]}" + puts " Local coordinates relative to intersecting entity (#{intersecting_entity_identifier}): #{coords[:local]}" + end + puts "#{intersecting_entities.size} intersecting entities found." + puts "##################" + end + + ## below implementation to export data in json format + ## Create the JSON hash with additional information (optional) + ## Uncomment the following lines if you wan to generate and export data in JSON format + # exportable = { "model_name": model.name, "entities": extract_model_objects(model) } + # puts exportable + # filename = "diameters.json" + # output_path = File.join("