Staircase generator plug-in

This topic has been viewed 3500474 times and has 98 replies
Thread status: Active
Test the features and bug fixes of future versions [img]http://www.sweethome3d.com/images/flags/en_small.gif[/img]
User avatar
Xlance
Posts: 144
Joined: Mon Mar 23, 2020 8:36 am

Re: Staircase generator plug-in

Post by Xlance »

@el_dario
Version 4.2 or later will be posted on sourceforge as soon as it's ready [:)]

Windows 64bit | Intel i7 | SH3D 8.0 + Plugins
el_dario
Posts: 2
Joined: Fri Jun 26, 2026 12:30 pm

Re: Staircase generator plug-in

Post by el_dario »

Thank you for your reply, @Xlance! I eagerly await the release of version 4.2, so I can try out these new features! :)
User avatar
Xlance
Posts: 144
Joined: Mon Mar 23, 2020 8:36 am

Re: Staircase generator plug-in

Post by Xlance »

NEW in version 4.2
Import Plan image of Staircase in 2D Shape Designer and Trace it to get Precise cutout *.svg path

1.CHOOSE Plan image of Staircase to Trace

2.TRACE then SAVE values of custom cutout into *.svg

3.LOAD *.svg file then COPY values of custom cutout

4.PASTE values of custom cutout into field (make sure to un-select 'generate cutout' check box)

5.CREATE staircase (custom cutout will be embedded with model)



Take screenshot
https://ibb.co/Hp7L50hg

Open 2D Shape Designer and choose image to load
https://ibb.co/kgcqv5DS

Trace outline/cutout then save in *.svg
https://ibb.co/hJPJcnb7

Load *.svg and copy values
https://ibb.co/SjhZx5Z

Paste values into custom cutout field (make sure to un-select 'generate cutout' check box)
https://ibb.co/21Pkb45G

Result
https://ibb.co/3Y0zDr4F

Windows 64bit | Intel i7 | SH3D 8.0 + Plugins
User avatar
Keet
Posts: 1838
Joined: Fri Apr 08, 2022 7:13 am
Country: Netherlands

Re: Staircase generator plug-in

Post by Keet »

Why so difficult? You can easily calculate an SVG cutout string using the v lines in a room object (or any other object where an outline can be calculated from). It is much easier to draw a room then the screenshot copies. Here's the code for it in PHP and C#.
Both code listings are made to to be readable by non-programmers too. This was a test for me so a little optimization is probably possible.
The C# code is close to Java code so should be easy to convert.
The PHP code is amateur work, I only use it for my websites.

C# code:

Code: Select all

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace SH3D
{
	public class SVG
	{
		// constructor
		public SVG () {}

		/// <summary>
		/// Create an SVG cutout string from the given obj file
		/// </summary>
		/// <returns>
		/// The SVG cutout string
		/// </returns>
		/// <param name='sourcefile'>
		/// The obj file to use for creating the SVG string. 
		/// A cutout string is generated using the v lines in the first group, best result with a room object.
		/// </param>
		public string CreateSVGcutout (string sourcefile)
		{
			if (!File.Exists (sourcefile)) { return string.Empty; }

#region create vertice list
			// Create an empty list to hold all vertices (v lines)
			List<Vertice> vertices = new List<Vertice> ();

			// Read all lines in the obj file into a string array
			String[] lines = File.ReadAllLines (sourcefile);
			bool start = false;

			// For each line that starts with "v " create a Vertice object and add it to the vertices list.
			// z is set to 0 by default, not used for a SVG cutout.
			foreach (string line in lines) {
				if (line.StartsWith ("v ")) {
					start = true;
					string[] parts = line.Split (new char[] {' '});
					// 0 = "v", 1=X, 2=Z, 3=Y
					Vertice v = new Vertice (double.Parse(parts [1]), double.Parse(parts [3]), 0.0d);
					vertices.Add (v);
				} else {
					// First set of v lines done, ignore the rest
					if (start) {
						break;
					}
				}
			}
#endregion create vertice list

#region object bounderies
			// Get the object bounderies, I.E. the width and depth of the object
			// These are needed to calculate the L points
			// Calculating this enables processing objects that dont't have 0,0 as the position coordinates.
			double minwidth = 0.0;
			double maxwidth = 0.0;
			double mindepth = 0.0;
			double maxdepth = 0.0;
			bool first = true;
			foreach (Vertice v in vertices) {
				if (first) {
					minwidth = v.X;
					maxwidth = v.X;
					mindepth = v.Y;
					maxdepth = v.Y;
					first = false;
				} else {
					minwidth = Math.Min (minwidth, v.X);
					maxwidth = Math.Max (maxwidth, v.X);
					mindepth = Math.Min (mindepth, v.Y);
					maxdepth = Math.Max (maxdepth, v.Y);
				}
			}

			double width = 0.0d;
			if (minwidth < 0) {
				if (maxwidth < 0) {	// - / -
					width = Math.Abs (minwidth) - Math.Abs (maxwidth);
				} else { // - / +
					width = Math.Abs (minwidth) + maxwidth;
				}
			} else { // + / +
				width = maxwidth - minwidth;
			}

			double depth = 0.0d;
			if (mindepth < 0) {
				if (maxdepth < 0) { // - / -
					depth = Math.Abs (mindepth) - Math.Abs (maxdepth);
				} else { // - / +
					depth = Math.Abs (mindepth) + Math.Abs (maxdepth);
				}
			} else { // + / +
				depth = maxdepth - mindepth;
			}

			// Recalculate the vertice points so they start from the correct origin
			for (int i=0; i< vertices.Count; i++) {
				vertices[i].X = vertices[i].X-minwidth;
				vertices[i].Y = vertices[i].Y-mindepth;
			}
#endregion object bounderies

#region create SVG string
			string svg = "";
			first = true;
			foreach (Vertice v in vertices) {
				if (first) {
					// first starts with "M"
					svg += "M" + Math.Round(v.X/width,4).ToString() + "," + Math.Round(v.Y/depth,4).ToString() + " ";
					first = false;
				} else {
					// add L points
					svg += "L" + Math.Round(v.X/width,4).ToString() + "," + Math.Round(v.Y/depth,4).ToString() + " ";
				}
			}
			svg += "z";	// z = back to start (M)
#endregion create SVG string

			// return the SVG string
			return svg;
		}
	}
}
PHP code:

Code: Select all

<?php
include_once "base.php";
if (session_id() == "") { session_start(); }

class SVGCutout {
//	private $_exportpath = TMPPATH;
	public $lines;

	function __construct() {}

	// CEILING&WALL cutout
	function CreateSVGcutout(): string
	{
		$vertices = array();
		$start = false;
		foreach(preg_split("/((\r?\n)|(\r\n?))/", $this->lines) as $line) {
			if (str_starts_with($line, 'v ')) {
				$start = true;
				$parts = preg_split('/\s+/', $line);
				// 0 = "v", 1=X, 2=Z, 3=Y
				$vertices[] = array($parts[1],$parts[3]);
			} else {
				if ($start === true) {
					// ignore the rest.
					break;
				}
			}
		}

		// get the object bounderies, I.E. the width and depth of the object
		// these are needed to calculate the L points
		$minw = 0.0; $maxw = 0.0; $mind = 0.0; $maxd = 0.0;
		$first = true;

		// get the object boundaries
		for ($i = 0; $i < count($vertices); $i++) {
			if ($first === true) {
				$minw = (float)$vertices[$i][0];
				$maxw = (float)$vertices[$i][0];
				$mind = (float)$vertices[$i][1];
				$maxd = (float)$vertices[$i][1];
				$first = false;
			} else {
				$minw = min($minw, (float)$vertices[$i][0]);
				$maxw = max($maxw, (float)$vertices[$i][0]);
				$mind = min($mind, (float)$vertices[$i][1]);
				$maxd = max($maxd, (float)$vertices[$i][1]);
			}
		}

		// calculate the width and depth
		$width = 0.0;
		$depth = 0.0;
		if ($minw < 0) {
			if ($maxw < 0) { $width = abs($minw) - abs($maxw); }
			else { $width = abs($minw) + $maxw;	}
		} else { $width = $maxw - $minw; }
		if ($mind < 0) {
			if ($maxd < 0) { $depth = abs($mind) - abs($maxd); }
			else { $depth = abs($mind) + abs($maxd); }
		} else { $depth = $maxd - $mind; }

		// to avoid division by zero if either one gets to be 0
		if ($width == 0) { return "The width is 0"; }
		if ($depth == 0) { return "The depth is 0";	}

		// create the svg string
		$svg = "";
		$first = true;
		for ($i = 0; $i < count($vertices); $i++) {
			// recalculate the vertice points so they start from the correct origin
			$vertices[$i][0] = $vertices[$i][0]-$minw;
			$vertices[$i][1] = $vertices[$i][1]-$mind;
			// calculate the M and L values
			if ($first === true) { $svg .= "M"; $first = false;	}
			else { $svg .= "L"; }
			$svg .= (string)round(($vertices[$i][0])/$width,4) . "," . (string)round(($vertices[$i][1])/$depth,4) . " ";
		}
		$svg .= "z";
	
//		$fs = fopen($this->_exportpath . "svgoutput.txt", "w");
//		fwrite($fs,$svg);
//		fclose($fs);

		return $svg;
	}
}
?>
Dodecagon.nl
1300+ 3D models, manuals, and projects
User avatar
Xlance
Posts: 144
Joined: Mon Mar 23, 2020 8:36 am

Re: Staircase generator plug-in

Post by Xlance »

An excellent solution. Thank You [:)]
However in many cases I need to get creative and design a cutout which does not correspond to model edges. e.g. circular for a T shaped

Windows 64bit | Intel i7 | SH3D 8.0 + Plugins
User avatar
Keet
Posts: 1838
Joined: Fri Apr 08, 2022 7:13 am
Country: Netherlands

Re: Staircase generator plug-in

Post by Keet »

That's why this code solution is easier. You can draw a room in any form you need, for example a star or T and it will calculate the cutout. To be clear: this is independent from the staircase object, completely separate. It is as if you draw the outline as in your examples but now without screenshots and as a room object.
Dodecagon.nl
1300+ 3D models, manuals, and projects
User avatar
Xlance
Posts: 144
Joined: Mon Mar 23, 2020 8:36 am

Re: Staircase generator plug-in

Post by Xlance »

▼
Version 4.2 Released on Sourceforge
(This update took a LOT of time and effort ;-)
Enjoy

https://sourceforge.net/p/sweethome3d/plug-ins/60/#a9db

Windows 64bit | Intel i7 | SH3D 8.0 + Plugins
nckv
Posts: 2
Joined: Tue Feb 20, 2024 3:17 pm

Re: Staircase generator plug-in

Post by nckv »

Hi, thanks for this nice plugin! Could you maybe add a function to make the "first stairs count" asymmetric, i.e. different counts for the bottom treads and the top treads?

So basically add also a "last stairs count" or something? Or maybe better names are "lower tread count" and "upper tread count"?

I have a staircase where this is like that.

Also, the latest version 4.2 is not detected by the Plugin Manager plugin yet!
User avatar
dorin
Posts: 835
Joined: Thu Apr 24, 2014 9:14 am
Country: Vatra Dornei, Suceava, Romania

Re: Staircase generator plug-in

Post by dorin »

Maybe this could help you.
A computer program does what you tell it to do, not what you want it to do. Murphy's Law
When all else fails, read the instructions. Murphy's Law
If you don't like "AS IS", DIY. Dorin's law
Post Reply New Topic

Who is online

Users browsing this forum: No registered users