Print at Aug 20, 2026, 9:33:14 PM

Posted by Keet at Aug 7, 2026, 9:53:06 AM
Re: Staircase generator plug-in
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:
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.X = vertices.X-minwidth;
vertices.Y = vertices.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:
<?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