Skip to content

Commit

Permalink
Finished the interior shape transform. Created layer objects to manag…
Browse files Browse the repository at this point in the history
…e the entities in various layers, such as Walls and Outline.
  • Loading branch information
knicholson32 committed Oct 15, 2023
1 parent 359441f commit 37af5ff
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 138 deletions.
157 changes: 157 additions & 0 deletions src/lib/layers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { Svg } from '@svgdotjs/svg.js';
import type * as Types from '../types';
import { G } from '@svgdotjs/svg.js';
import { Entity, PathWrapper, Surface } from '../shapes';
import * as helpers from '../helpers';
import { PointArray } from '@svgdotjs/svg.js';
import { Path } from '@svgdotjs/svg.js';

export class Layer {

layer: G;

static layers: Layer[] = [];

static getAllEntities() {
const entities: Entity[] = [];
for (const layer of Layer.layers) entities.push(...layer.getEntities());
return entities;
}

static get() {
return this.layers;
}

constructor(stage: Svg, id: string) {
this.layer = stage.group();
this.layer.id(id);
Layer.layers.push(this);
}

front() {
this.layer.front();
}

centerScale(_center: Types.Point, _scale: number) {}

getEntities(): Entity[] {
return [];
}

}



export class Walls extends Layer {

walls: Surface[];

constructor(stage: Svg) {
super(stage, 'Walls');
this.walls = [];
}

addWall(point1: Types.Point, point2: Types.Point) {
this.walls.push(new Surface(this.layer, point1, point2));
}

add(points: PointArray) {
// Make sure the last point goes back to the first point
if (points[0][0] !== points[points.length-1][0]) points.push(points[0]);
let trailingPoint = helpers.arrayXYToPoint(points[0]);
for (let i = 1; i < points.length; i++) {
const point = helpers.arrayXYToPoint(points[i]);
this.addWall(trailingPoint, point);
trailingPoint = point;
}
}

getBoundingBox() {
let minX = Infinity, minY = Infinity;
let maxX = -Infinity, maxY = -Infinity;
for (const surface of this.walls) {
if (surface.point1.x < minX) minX = surface.point1.x;
if (surface.point2.x < minX) minX = surface.point2.x;
if (surface.point1.x > maxX) maxX = surface.point1.x;
if (surface.point2.x > maxX) maxX = surface.point2.x;
if (surface.point1.y < minY) minY = surface.point1.y;
if (surface.point1.y < minY) minY = surface.point1.y;
if (surface.point2.y > maxY) maxY = surface.point2.y;
if (surface.point2.y > maxY) maxY = surface.point2.y;
}
return { minX, minY, maxX, maxY };
}

centerScale(center: Types.Point, scale: number) {
// Move the floorplan to the 0,0 position
let mm = this.getBoundingBox();
const translate = {
x: -mm.minX - (mm.maxX - mm.minX) / 2,
y: -mm.minY - (mm.maxY - mm.minY) / 2
};
for (const entity of this.walls) entity.basisTranslate(translate);
// TODO: Calculate this better
for (const entity of this.walls) entity.basisScale(scale);

// mm = getMinMaxCoords(surfaces);
// stage.polygon([mm.minX,mm.minY, mm.minX,mm.maxY, mm.maxX,mm.maxY, mm.maxX,mm.minY, mm.minX,mm.minY]).fill({opacity: 0}).stroke({color: '#f00', width: 3});
// The transformations will all be done around 0,0. After translations, the running translate points will move the
// shape to be centered around the specified location
for (const entity of this.walls) entity.runningTranslate(center);
}

getEntities(): Entity[] {
return this.walls;
}

}


export class Rooms extends Layer {

constructor(stage: Svg) {
super(stage, 'Rooms');
}

add(points: PointArray) {
if (points[0][0] !== points[points.length - 1][0]) points.push(points[0]);
}

}

export class Interior extends Layer {

path: PathWrapper | undefined;

constructor(stage: Svg) {
super(stage, 'Interior');
}

set(p: Path) {
this.path = new PathWrapper(p, this.layer);
}

centerScale(center: Types.Point, scale: number) {
if (this.path === undefined) return;
this.path.basisScale(scale);
this.path.runningTranslate(center);
}

getEntities(): Entity[] {
if (this.path === undefined) return [];
else return [this.path];
}

}

export class Outline extends Layer {

constructor(stage: Svg) {
super(stage, 'Interior');
}

set(points: PointArray) {
if (points[0][0] !== points[points.length - 1][0]) points.push(points[0]);
}

}
105 changes: 38 additions & 67 deletions src/lib/parser/index.ts
Original file line number Diff line number Diff line change
@@ -1,90 +1,64 @@
import { G, Path, PointArray, Polygon, Polyline, SVG, Svg } from '@svgdotjs/svg.js';
import { Entity, PathWrapper, Surface } from '../shapes';
import { Entity } from '../shapes';
import type * as Types from '../types';
import * as tools from '../tools';
import * as helpers from '../helpers';
import * as layer from '../layers';
// import * as helpers from '../helpers';
import '@svgdotjs/svg.topoly.js';
import { Rect } from '@svgdotjs/svg.js';

const rectToPoints = (rect: Rect) => {
const x = new Number(rect.x()).valueOf();
const y = new Number(rect.y()).valueOf();
const w = new Number(rect.width()).valueOf();
const h = new Number(rect.height()).valueOf();
return new PointArray([
x, y,
x + w, y,
x + w, y + h,
x, y + h,
x, y,
]);
}

const pointsToSurfaces = (points: PointArray, stage: Svg, addFirstAgain = false): Surface[] => {
let trailingPoint = helpers.arrayXYToPoint(points[0]);
if (addFirstAgain) points.push(points[0]);
const surfaces: Surface[] = [];
for (let i = 1; i < points.length; i++) {
const point = helpers.arrayXYToPoint(points[i]);
surfaces.push(new Surface(stage, trailingPoint, point));
trailingPoint = point;
}
return surfaces;
}

const parseFloorplan = (group: G, stage: Svg) => {
const elements = group.children();
const parseFloorplan = (src: G, interior: layer.Interior, outline: layer.Outline, walls: layer.Walls) => {
const elements = src.children();
const entities: Entity[] = [];
for (const element of elements) {
const id = element.id().replace(/_x5F/gm, '');
console.log(`Adding '${id}'`);
if (id === 'outline') {
let points: PointArray;
if (element instanceof Path) {
const pointArray = element.toPoly()
entities.push(...pointsToSurfaces(pointArray.array(), stage));
} else if (element instanceof Polygon) {
entities.push(...pointsToSurfaces(element.array(), stage, true));
} else if (element instanceof Polyline) {
entities.push(...pointsToSurfaces(element.array(), stage));
points = element.toPoly().array();
} else if (element instanceof Polygon || element instanceof Polyline) {
points = element.array();
} else if (element instanceof Rect) {
entities.push(...pointsToSurfaces(rectToPoints(element), stage));
points = tools.rectToPoints(element);
} else {
console.warn(`Invalid Shape Type: '${element.type}'\nElement named '${id}' could not be parsed as an outline because it was not a Rect, Path, Polygon or Polyline. `);
continue;
}
walls.add(points);
outline.set(points);
} else if (id === 'interior') {
// stage.add(element.fill({color: '#fff'}));
// console.log(stage)
if (element instanceof Path) {
entities.push(new PathWrapper(element, stage));
interior.set(element);
} else {
console.warn(`Invalid Shape Type: '${element.type}'\nElement named '${id}' could not be parsed as an interior because it was not a Path. `);
}

}
}
return entities;
}

// TODO: Make room objects
const parseRooms = (group: G, stage: Svg) => {
const elements = group.children();
const parseRooms = (src: G, rooms: layer.Rooms, walls: layer.Walls) => {
const elements = src.children();
const entities: Entity[] = [];
for (const element of elements) {
const id = element.id().replace(/_x5F/gm, '');
if (element instanceof G) {
console.log(`Adding room group '${id}'`);
entities.push(...parseRooms(element, stage));
parseRooms(element, rooms, walls);
} else {
console.log(`Adding room '${id}'`);
if (element instanceof Polygon) {
entities.push(...pointsToSurfaces(element.array(), stage, true));
} else if (element instanceof Polyline) {
entities.push(...pointsToSurfaces(element.array(), stage));
let points: PointArray;
if (element instanceof Polygon || element instanceof Polyline) {
points = element.array();
} else if (element instanceof Rect) {
entities.push(...pointsToSurfaces(rectToPoints(element), stage));
points = tools.rectToPoints(element);
} else {
console.warn(`Invalid Shape Type: '${element.type}'\nElement named '${id}' could not be parsed as a room because it was not a Rect, Polygon or Polyline. `);
continue;
}
rooms.add(points);
walls.add(points);
}
}
return entities;
Expand All @@ -97,22 +71,25 @@ export const parse = (stage: Svg, svgSize: Types.Point, inputSVG: string) => {
// Get the elements inside the SVG file. We have to do 'children()' twice because the top-level
// child is just the SVG tag itself
const elements = s.children()[0].children();
console.log(elements);

const entities: Entity[] = [];

console.log(elements);
const walls = new layer.Walls(stage);
const rooms = new layer.Rooms(stage);
const interior = new layer.Interior(stage);
const outline = new layer.Outline(stage);

for (const element of elements) {

if (element instanceof G) {
const id = element.id();
switch (id) {
case 'plan':
entities.push(...parseFloorplan(element, stage));
parseFloorplan(element, interior, outline, walls);
break;
case 'room':
case 'rooms':
entities.push(...parseRooms(element, stage));
parseRooms(element, rooms, walls);
break;
case 'doors':
case 'door':
Expand All @@ -121,21 +98,15 @@ export const parse = (stage: Svg, svgSize: Types.Point, inputSVG: string) => {
console.warn(`Unknown group name '${id}'`);
}
}


// // console.log(element);
// if (element instanceof Polyline) entities.concat(pointsToSurfaces(element.array(), stage));
// if (element instanceof Polygon) entities.concat(pointsToSurfaces(element.array(), stage, true));
// if (element instanceof Path) {
// // console.log(element);
// // console.log(element.array());
// }
}

const surfaces = entities.filter((e) => e instanceof Surface) as Surface[];
tools.centerScale(surfaces, svgSize);

// console.log(entities);
const center = {
x: svgSize.x / 2,
y: svgSize.y / 2
}

return entities;
walls.centerScale(center, 0.75);
rooms.centerScale(center, 0.75);
interior.centerScale(center, 0.75);
}
Loading

0 comments on commit 37af5ff

Please sign in to comment.