mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 08:25:19 +01:00
263 lines
No EOL
7.1 KiB
TypeScript
263 lines
No EOL
7.1 KiB
TypeScript
import * as sm from './sketchModel'
|
|
import {Graph} from '../../utils/graph'
|
|
import {HashTable} from '../../utils/hashmap'
|
|
import Joints from 'gems/joints';
|
|
import sketchObjectGlobalId from './sketchObjectGlobalId';
|
|
import VectorFactory from 'math/vectorFactory';
|
|
import {strictEqual2D} from "math/equality";
|
|
import {Contour, Segment, SketchPrimitive} from "./sketchModel";
|
|
import Vector from "math/vector";
|
|
|
|
export class SketchGeom {
|
|
|
|
connections: SketchPrimitive[];
|
|
loops: SketchPrimitive[];
|
|
_contours: Contour[];
|
|
points: any[];
|
|
|
|
constructor() {
|
|
this.connections = [];
|
|
this.loops = [];
|
|
this._contours = null;
|
|
this.points = [];
|
|
}
|
|
|
|
fetchContours() {
|
|
if (this._contours === null) {
|
|
this._contours = FetchContours(this);
|
|
}
|
|
return this._contours;
|
|
}
|
|
|
|
get contours(): Contour[] {
|
|
return this.fetchContours();
|
|
}
|
|
|
|
getAllObjects() {
|
|
return [...this.connections, ...this.loops];
|
|
}
|
|
}
|
|
|
|
export function ReadSketch(sketch, sketchId) {
|
|
const getID = obj => sketchObjectGlobalId(sketchId, obj.id);
|
|
const out = new SketchGeom();
|
|
|
|
const coiJoints = new Joints();
|
|
|
|
if (sketch.constraints !== undefined) {
|
|
for (let i = 0; i < sketch.constraints.length; ++i) {
|
|
const c = sketch.constraints[i];
|
|
const name = c[0];
|
|
const ps = c[1];
|
|
if (name === 'coi') {
|
|
coiJoints.connect(ps[0], ps[1]);
|
|
}
|
|
}
|
|
}
|
|
const vectorFactory = new VectorFactory();
|
|
const pointsById = new Map();
|
|
function ReadSketchPoint(pt): Vector {
|
|
return vectorFactory.create(pt.x, pt.y, 0);
|
|
}
|
|
if (sketch.version !== 3) {
|
|
return out;
|
|
}
|
|
for (const obj of sketch.objects) {
|
|
const isConstructionObject = obj.role === 'construction';
|
|
|
|
// if (isConstructionObject && obj._class !== 'TCAD.TWO.Segment') continue;
|
|
const data = obj.data;
|
|
let createdObj: SketchPrimitive;
|
|
if (obj.type === 'Segment') {
|
|
const segA = ReadSketchPoint(data.a);
|
|
const segB = ReadSketchPoint(data.b);
|
|
createdObj = new sm.Segment(getID(obj), segA, segB);
|
|
out.connections.push(createdObj);
|
|
} else if (obj.type === 'Arc') {
|
|
const arcA = ReadSketchPoint(data.a);
|
|
const arcB = ReadSketchPoint(data.b);
|
|
const arcCenter = ReadSketchPoint(data.c);
|
|
createdObj = new sm.Arc(getID(obj), arcA, arcB, arcCenter);
|
|
out.connections.push(createdObj);
|
|
} else if (obj.type === 'EllipticalArc') {
|
|
if (data.ep1) {
|
|
continue;
|
|
}
|
|
const c = ReadSketchPoint(data.c);
|
|
const rx = readSketchFloat(data.rx);
|
|
const ry = readSketchFloat(data.ry);
|
|
const rot = readSketchFloat(data.rot);
|
|
const a = ReadSketchPoint(data.a);
|
|
const b = ReadSketchPoint(data.b);
|
|
createdObj = new sm.EllipticalArc(getID(obj), c, rx, ry, rot, a, b);
|
|
out.loops.push(createdObj);
|
|
} else if (obj.type === 'BezierCurve') {
|
|
const a = ReadSketchPoint(data.cp1);
|
|
const b = ReadSketchPoint(data.cp4);
|
|
const cp1 = ReadSketchPoint(data.cp2);
|
|
const cp2 = ReadSketchPoint(data.cp3);
|
|
createdObj = new sm.BezierCurve(getID(obj), a, b, cp1, cp2);
|
|
out.connections.push(createdObj);
|
|
} else if (obj.type === 'Circle') {
|
|
const circleCenter = ReadSketchPoint(data.c);
|
|
createdObj = new sm.Circle(getID(obj), circleCenter, readSketchFloat(data.r));
|
|
out.loops.push(createdObj);
|
|
} else if (obj.type === 'Ellipse') {
|
|
if (data.ep1) {
|
|
continue;
|
|
}
|
|
const c = ReadSketchPoint(data.c);
|
|
const rx = readSketchFloat(data.rx);
|
|
const ry = readSketchFloat(data.ry);
|
|
const rot = readSketchFloat(data.rot);
|
|
createdObj = new sm.Ellipse(getID(obj), c, rx, ry, rot);
|
|
out.loops.push(createdObj);
|
|
} else if (obj.type === 'Point') {
|
|
if (data.ep1) {
|
|
continue;
|
|
}
|
|
|
|
ReadSketchPoint(data);
|
|
const x = readSketchFloat(data.x);
|
|
const y = readSketchFloat(data.y);
|
|
const z = 0;
|
|
|
|
//out.points.push(ReadSketchPoint(data));
|
|
createdObj = {
|
|
id:getID(obj),
|
|
point:{x,y,z}
|
|
} as any;
|
|
out.points.push(createdObj);
|
|
}
|
|
createdObj.construction = isConstructionObject;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
export function FetchContours(geom): Contour[] {
|
|
const contours = findClosedContours(geom.connections.filter(c => !c.construction));
|
|
for (const loop of geom.loops) {
|
|
if (loop.construction) {
|
|
continue;
|
|
}
|
|
const contour = new sm.Contour();
|
|
contour.add(loop);
|
|
contours.push(contour);
|
|
}
|
|
for (const contour of contours) {
|
|
if (!contour.isCCW()) {
|
|
contour.reverse();
|
|
}
|
|
}
|
|
return contours;
|
|
}
|
|
|
|
function findClosedContours(segments) {
|
|
const result = [];
|
|
findClosedContoursFromPairedCurves(segments, result);
|
|
findClosedContoursFromGraph(segments, result);
|
|
return result;
|
|
}
|
|
|
|
function findClosedContoursFromPairedCurves(segments, result) {
|
|
for (let i = 0; i < segments.length; i++) {
|
|
const s1 = segments[i];
|
|
for (let j = i; j < segments.length; j++) {
|
|
if (i === j) continue;
|
|
const s2 = segments[j];
|
|
if (s1.isCurve || s2.isCurve) {
|
|
let paired = false;
|
|
if (strictEqual2D(s1.a, s2.a) && strictEqual2D(s1.b, s2.b)) {
|
|
paired = true;
|
|
s2.invert();
|
|
} else if (strictEqual2D(s1.a, s2.b) && strictEqual2D(s1.b, s2.a)) {
|
|
paired = true;
|
|
}
|
|
if (paired) {
|
|
const contour = new sm.Contour();
|
|
contour.add(s1);
|
|
contour.add(s2);
|
|
result.push(contour);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function findClosedContoursFromGraph(segments, result) {
|
|
|
|
const dict = HashTable.forVector2d();
|
|
const edges = HashTable.forDoubleArray();
|
|
|
|
function edgeKey(a, b) {
|
|
return [a.x, a.y, b.x, b.y];
|
|
}
|
|
|
|
const points = [];
|
|
function memDir(a, b) {
|
|
let dirs = dict.get(a);
|
|
if (dirs === null) {
|
|
dirs = [];
|
|
dict.put(a, dirs);
|
|
points.push(a);
|
|
}
|
|
dirs.push(b);
|
|
}
|
|
|
|
for (const seg of segments) {
|
|
const a = seg.a;
|
|
const b = seg.b;
|
|
|
|
memDir(a, b);
|
|
memDir(b, a);
|
|
edges.put(edgeKey(a, b), seg);
|
|
}
|
|
|
|
const graph = {
|
|
|
|
connections : function(e) {
|
|
const dirs = dict.get(e);
|
|
return dirs === null ? [] : dirs;
|
|
},
|
|
|
|
at : function(index) {
|
|
return points[index];
|
|
},
|
|
|
|
size : function() {
|
|
return points.length;
|
|
}
|
|
};
|
|
|
|
const loops = Graph.findAllLoops(graph, dict.hashCodeF, dict.equalsF);
|
|
for (const loop of loops) {
|
|
const contour = new sm.Contour();
|
|
for (let pi = 0; pi < loop.length; ++pi) {
|
|
const point = loop[pi];
|
|
const next = loop[(pi + 1) % loop.length];
|
|
let edge = edges.get(edgeKey(point, next));
|
|
if (edge === null) {
|
|
edge = edges.get(edgeKey(next, point));
|
|
edge.invert();
|
|
}
|
|
contour.add(edge);
|
|
}
|
|
result.push(contour);
|
|
}
|
|
}
|
|
|
|
const READ_AS_IS = v => v;
|
|
const READ_AS_INT = v => Math.round(v);
|
|
|
|
export let readSketchFloat = READ_AS_IS;
|
|
|
|
export function setSketchPrecision(precision) {
|
|
if (precision === undefined) {
|
|
readSketchFloat = READ_AS_IS;
|
|
} else if (precision === 0) {
|
|
readSketchFloat = READ_AS_INT;
|
|
} else {
|
|
const factor = Math.pow(10, precision);
|
|
readSketchFloat = v => Math.round(v * factor) / factor;
|
|
}
|
|
} |