Merge branch 'master' into python

pull/493/head
KmolYuan 2019-09-20 15:14:15 +08:00
commit 813217bf00
76 changed files with 2063 additions and 1160 deletions

View File

@ -10,22 +10,19 @@ ColumnLimit: 100
# Based on minimizing diff when applying clang-format # Based on minimizing diff when applying clang-format
AccessModifierOffset: -4 AccessModifierOffset: -4
DerivePointerAlignment: true
AlignConsecutiveAssignments: true AlignConsecutiveAssignments: true
FixNamespaceComments: true
NamespaceIndentation: Inner
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes # MultiLine
SpaceAfterTemplateKeyword: false
MaxEmptyLinesToKeep: 2
IndentPPDirectives: AfterHash
AlignEscapedNewlines: DontAlign AlignEscapedNewlines: DontAlign
BreakConstructorInitializers: BeforeColon AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true AllowShortCaseLabelsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
# This one is mixed in its effect: it permits some single-line functions, AlwaysBreakTemplateDeclarations: Yes # MultiLine
# but also tends to put, e.g., enums on a single line. BreakConstructorInitializers: BeforeColon
AllowShortBlocksOnASingleLine: true DerivePointerAlignment: true
FixNamespaceComments: true
IndentPPDirectives: AfterHash
MaxEmptyLinesToKeep: 2
NamespaceIndentation: Inner
SpaceAfterTemplateKeyword: false
# No way to remove all space around operators as seen in much of the code I looked at. # No way to remove all space around operators as seen in much of the code I looked at.
#SpaceBeforeAssignmentOperators: false #SpaceBeforeAssignmentOperators: false
@ -41,3 +38,4 @@ SortUsingDeclarations: false
# Hard to tell what the desired config here was. # Hard to tell what the desired config here was.
# AllowShortFunctionsOnASingleLine: Inline # AllowShortFunctionsOnASingleLine: Inline
AllowShortFunctionsOnASingleLine: None

View File

@ -5,6 +5,7 @@ Changelog
--- ---
New sketch features: New sketch features:
* New groups, revolution and helical extrusion.
* Extrude, lathe, translate and rotate groups can use the "assembly" * Extrude, lathe, translate and rotate groups can use the "assembly"
boolean operation, to increase performance. boolean operation, to increase performance.
* The solid model of extrude and lathe groups can be suppressed, * The solid model of extrude and lathe groups can be suppressed,
@ -52,6 +53,7 @@ New export/import features:
exported. This format allows to easily hack on triangle mesh data created exported. This format allows to easily hack on triangle mesh data created
in SolveSpace, supports colour information and is more space efficient than in SolveSpace, supports colour information and is more space efficient than
most other formats. most other formats.
* VRML (WRL) triangle meshes can now be exported, useful for e.g. [KiCAD](http://kicad.org).
* Export 2d section: custom styled entities that lie in the same * Export 2d section: custom styled entities that lie in the same
plane as the exported section are included. plane as the exported section are included.
@ -68,6 +70,10 @@ New measurement/analysis features:
* New command for measuring center of mass, with live updates as the sketch * New command for measuring center of mass, with live updates as the sketch
changes, "Analyze → Center of Mass". changes, "Analyze → Center of Mass".
* New option for displaying areas of closed contours. * New option for displaying areas of closed contours.
* When calculating volume of the mesh, volume of the solid from the current
group is now shown alongside total volume of all solids.
* When calculating area, and faces are selected, calculate area of those faces
instead of the closed contour in the sketch.
* When selecting a point and a line, projected distance to current * When selecting a point and a line, projected distance to current
workplane is displayed. workplane is displayed.

@ -1 +1 @@
Subproject commit fe71514837af1c627e9e1054d1aa0193a06d3488 Subproject commit 880db1d34706778f216a2308fd82a9a3adacb314

3
pkg/snap/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.snap
solvespace-snap-src
squashfs-root

12
pkg/snap/build.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh -xe
dir="$(dirname "$(readlink -f "$0")")"
solvespace_snap_src="$dir/solvespace-snap-src"
trap "rm -rf $solvespace_snap_src" EXIT
cd "$dir"
git_root="$(git rev-parse --show-toplevel)"
rsync --filter=":- .gitignore" -r "$git_root"/ "$solvespace_snap_src"
snapcraft "$@"

View File

@ -0,0 +1,76 @@
name: solvespace
base: core18
summary: Parametric 2d/3d CAD
adopt-info: solvespace
description: |
SOLVESPACE is a free (GPLv3) parametric 3d CAD tool.
Applications include
* modeling 3d parts — draw with extrudes, revolves, and Boolean (union / difference) operations
* modeling 2d parts — draw the part as a single section, and export DXF, PDF, SVG; use 3d assembly to verify fit
* 3d-printed parts — export the STL or other triangle mesh expected by most 3d printers
* preparing CAM data — export 2d vector art for a waterjet machine or laser cutter; or generate STEP or STL, for import into third-party CAM software for machining
* mechanism design — use the constraint solver to simulate planar or spatial linkages, with pin, ball, or slide joints
* plane and solid geometry — replace hand-solved trigonometry and spreadsheets with a live dimensioned drawing
confinement: strict
license: GPL-3.0
layout:
/usr/share/solvespace:
bind: $SNAP/usr/share/solvespace
apps:
solvespace:
command: usr/bin/solvespace
desktop: solvespace.desktop
extensions: [gnome-3-28]
plugs: [opengl, unity7, home, removable-media, gsettings, network]
environment:
__EGL_VENDOR_LIBRARY_DIRS: $SNAP/usr/share/glvnd/egl_vendor.d
cli:
command: usr/bin/solvespace-cli
plugs: [home, removable-media, network]
parts:
solvespace:
plugin: cmake
source: ./solvespace-snap-src
source-type: local
override-pull: |
snapcraftctl pull
version_major=$(grep "solvespace_VERSION_MAJOR" CMakeLists.txt | tr -d "()" | cut -d" " -f2)
version_minor=$(grep "solvespace_VERSION_MINOR" CMakeLists.txt | tr -d "()" | cut -d" " -f2)
version="$version_major.$version_minor~$(git rev-parse --short=8 HEAD)"
snapcraftctl set-version "$version"
git describe --exact-match HEAD && grade="stable" || grade="devel"
snapcraftctl set-grade "$grade"
git submodule update --init extlib/libdxfrw extlib/flatbuffers extlib/q3d
configflags:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
- -DENABLE_TESTS=OFF
- -DSNAP=ON
build-packages:
- zlib1g-dev
- libpng-dev
- libcairo2-dev
- libfreetype6-dev
- libjson-c-dev
- libfontconfig1-dev
- libgtkmm-3.0-dev
- libpangomm-1.4-dev
- libgl-dev
- libglu-dev
- libspnav-dev
- git
stage-packages:
- libspnav0
- libatkmm-1.6-1v5
- libcairomm-1.0-1v5
- libgtkmm-3.0-1v5
- libglibmm-2.4-1v5
- libpangomm-1.4-1v5
- libsigc++-2.0-0v5
- libglew2.0
- libegl-mesa0
- libdrm2

View File

@ -138,6 +138,13 @@ else()
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
RENAME com.solvespace.SolveSpace-slvs.xml) RENAME com.solvespace.SolveSpace-slvs.xml)
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps
RENAME com.solvespace.SolveSpace.svg)
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes
RENAME com.solvespace.SolveSpace.svg)
foreach(SIZE 16x16 24x24 32x32 48x48) foreach(SIZE 16x16 24x24 32x32 48x48)
install(FILES freedesktop/solvespace-${SIZE}.png install(FILES freedesktop/solvespace-${SIZE}.png
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/apps DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/apps
@ -146,6 +153,22 @@ else()
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/mimetypes DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/mimetypes
RENAME com.solvespace.SolveSpace.png) RENAME com.solvespace.SolveSpace.png)
endforeach() endforeach()
elseif(SNAP)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/solvespace-snap.desktop
DESTINATION /
RENAME solvespace.desktop)
# snapd does not support registering new mime types
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION /meta/icons/hicolor/scalable/apps
RENAME snap.solvespace.svg)
foreach(SIZE 16x16 24x24 32x32 48x48)
install(FILES freedesktop/solvespace-${SIZE}.png
DESTINATION /meta/icons/hicolor/${SIZE}/apps
RENAME snap.solvespace.png)
endforeach()
else() else()
configure_file( configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/solvespace.desktop.in ${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/solvespace.desktop.in
@ -156,6 +179,13 @@ else()
install(FILES freedesktop/solvespace-mime.xml install(FILES freedesktop/solvespace-mime.xml
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
RENAME solvespace-slvs.xml) RENAME solvespace-slvs.xml)
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps
RENAME solvespace.svg)
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes
RENAME application.x-solvespace.svg)
foreach(SIZE 16x16 24x24 32x32 48x48) foreach(SIZE 16x16 24x24 32x32 48x48)
install(FILES freedesktop/solvespace-${SIZE}.png install(FILES freedesktop/solvespace-${SIZE}.png

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<g>
<path d="m116 152h84v12h-84z" fill="#cbcbcb"/>
<path d="m152 80h12v74h-12z" fill="#cbcbcb"/>
<path d="m104 140h84v12h-84z" fill="#e40cf2"/>
<path d="m140 68h12v74h-12z" fill="#e40cf2"/>
<path d="m56 164h36v36h-36z" fill="#43f20c"/>
<path d="m68 32h12v192h-12z"/>
<path d="m32 176h192v12h-192z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 444 B

View File

@ -0,0 +1,10 @@
[Desktop Entry]
Version=1.0
Name=SolveSpace
Comment=A parametric 2d/3d CAD
Exec=solvespace
MimeType=application/x-solvespace
Icon=${SNAP}/meta/icons/hicolor/scalable/apps/snap.solvespace.svg
Type=Application
Categories=Graphics
Keywords=parametric;cad;2d;3d;

View File

@ -12,26 +12,19 @@ SBsp2 *SBsp2::Alloc() { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); }
SBsp3 *SBsp3::Alloc() { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); } SBsp3 *SBsp3::Alloc() { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); }
SBsp3 *SBsp3::FromMesh(const SMesh *m) { SBsp3 *SBsp3::FromMesh(const SMesh *m) {
SBsp3 *bsp3 = NULL;
int i;
SMesh mc = {}; SMesh mc = {};
for(i = 0; i < m->l.n; i++) { for(auto const &elt : m->l) { mc.AddTriangle(&elt); }
mc.AddTriangle(&(m->l.elem[i]));
}
srand(0); // Let's be deterministic, at least! srand(0); // Let's be deterministic, at least!
int n = mc.l.n; int n = mc.l.n;
while(n > 1) { while(n > 1) {
int k = rand() % n; int k = rand() % n;
n--; n--;
swap(mc.l.elem[k], mc.l.elem[n]); swap(mc.l[k], mc.l[n]);
}
for(i = 0; i < mc.l.n; i++) {
bsp3 = InsertOrCreate(bsp3, &(mc.l.elem[i]), NULL);
} }
SBsp3 *bsp3 = NULL;
for(auto &elt : mc.l) { bsp3 = InsertOrCreate(bsp3, &elt, NULL); }
mc.Clear(); mc.Clear();
return bsp3; return bsp3;
} }

View File

@ -12,16 +12,16 @@ void SolveSpaceUI::Clipboard::Clear() {
} }
bool SolveSpaceUI::Clipboard::ContainsEntity(hEntity he) { bool SolveSpaceUI::Clipboard::ContainsEntity(hEntity he) {
if(he.v == Entity::NO_ENTITY.v) if(he == Entity::NO_ENTITY)
return true; return true;
ClipboardRequest *cr; ClipboardRequest *cr;
for(cr = r.First(); cr; cr = r.NextAfter(cr)) { for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
if(cr->oldEnt.v == he.v) if(cr->oldEnt == he)
return true; return true;
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) { for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
if(cr->oldPointEnt[i].v == he.v) if(cr->oldPointEnt[i] == he)
return true; return true;
} }
} }
@ -29,16 +29,16 @@ bool SolveSpaceUI::Clipboard::ContainsEntity(hEntity he) {
} }
hEntity SolveSpaceUI::Clipboard::NewEntityFor(hEntity he) { hEntity SolveSpaceUI::Clipboard::NewEntityFor(hEntity he) {
if(he.v == Entity::NO_ENTITY.v) if(he == Entity::NO_ENTITY)
return Entity::NO_ENTITY; return Entity::NO_ENTITY;
ClipboardRequest *cr; ClipboardRequest *cr;
for(cr = r.First(); cr; cr = r.NextAfter(cr)) { for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
if(cr->oldEnt.v == he.v) if(cr->oldEnt == he)
return cr->newReq.entity(0); return cr->newReq.entity(0);
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) { for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
if(cr->oldPointEnt[i].v == he.v) if(cr->oldPointEnt[i] == he)
return cr->newReq.entity(1+i); return cr->newReq.entity(1+i);
} }
} }
@ -170,9 +170,9 @@ void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
hRequest hr = he.request(); hRequest hr = he.request();
Request *r = SK.GetRequest(hr); Request *r = SK.GetRequest(hr);
if(r->type == Request::Type::ARC_OF_CIRCLE) { if(r->type == Request::Type::ARC_OF_CIRCLE) {
if(he.v == hr.entity(2).v) { if(he == hr.entity(2)) {
return hr.entity(3); return hr.entity(3);
} else if(he.v == hr.entity(3).v) { } else if(he == hr.entity(3)) {
return hr.entity(2); return hr.entity(2);
} }
} }
@ -287,7 +287,7 @@ void GraphicsWindow::MenuClipboard(Command id) {
} }
case Command::PASTE_TRANSFORM: { case Command::PASTE_TRANSFORM: {
if(SS.clipboard.r.n == 0) { if(SS.clipboard.r.IsEmpty()) {
Error(_("Clipboard is empty; nothing to paste.")); Error(_("Clipboard is empty; nothing to paste."));
break; break;
} }

View File

@ -59,12 +59,12 @@ std::string Constraint::DescriptionString() const {
void Constraint::DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA) void Constraint::DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA)
{ {
SK.constraint.ClearTags(); SK.constraint.ClearTags();
for(int i = 0; i < SK.constraint.n; i++) { for(auto &constraint : SK.constraint) {
ConstraintBase *ct = &(SK.constraint.elem[i]); ConstraintBase *ct = &constraint;
if(ct->type != type) continue; if(ct->type != type) continue;
if(ct->entityA.v != entityA.v) continue; if(ct->entityA != entityA) continue;
if(ct->ptA.v != ptA.v) continue; if(ct->ptA != ptA) continue;
ct->tag = 1; ct->tag = 1;
} }
SK.constraint.RemoveTagged(); SK.constraint.RemoveTagged();
@ -406,7 +406,7 @@ void Constraint::MenuConstrain(Command id) {
Entity *l0 = SK.GetEntity(gs.entity[0]), Entity *l0 = SK.GetEntity(gs.entity[0]),
*l1 = SK.GetEntity(gs.entity[1]); *l1 = SK.GetEntity(gs.entity[1]);
if((l1->group.v != SS.GW.activeGroup.v) || if((l1->group != SS.GW.activeGroup) ||
(l1->construction && !(l0->construction))) (l1->construction && !(l0->construction)))
{ {
swap(l0, l1); swap(l0, l1);
@ -433,10 +433,10 @@ void Constraint::MenuConstrain(Command id) {
"(symmetric about workplane)\n")); "(symmetric about workplane)\n"));
return; return;
} }
if(c.entityA.v == Entity::NO_ENTITY.v) { if(c.entityA == Entity::NO_ENTITY) {
// Horizontal / vertical symmetry, implicit symmetry plane // Horizontal / vertical symmetry, implicit symmetry plane
// normal to the workplane // normal to the workplane
if(c.workplane.v == Entity::FREE_IN_3D.v) { if(c.workplane == Entity::FREE_IN_3D) {
Error(_("A workplane must be active when constraining " Error(_("A workplane must be active when constraining "
"symmetric without an explicit symmetry plane.")); "symmetric without an explicit symmetry plane."));
return; return;
@ -466,7 +466,7 @@ void Constraint::MenuConstrain(Command id) {
case Command::VERTICAL: case Command::VERTICAL:
case Command::HORIZONTAL: { case Command::HORIZONTAL: {
hEntity ha, hb; hEntity ha, hb;
if(c.workplane.v == Entity::FREE_IN_3D.v) { if(c.workplane == Entity::FREE_IN_3D) {
Error(_("Activate a workplane (with Sketch -> In Workplane) before " Error(_("Activate a workplane (with Sketch -> In Workplane) before "
"applying a horizontal or vertical constraint.")); "applying a horizontal or vertical constraint."));
return; return;
@ -510,12 +510,10 @@ void Constraint::MenuConstrain(Command id) {
Entity *nfree = SK.GetEntity(c.entityA); Entity *nfree = SK.GetEntity(c.entityA);
Entity *nref = SK.GetEntity(c.entityB); Entity *nref = SK.GetEntity(c.entityB);
if(nref->group.v == SS.GW.activeGroup.v) { if(nref->group == SS.GW.activeGroup) {
swap(nref, nfree); swap(nref, nfree);
} }
if(nfree->group.v == SS.GW.activeGroup.v && if(nfree->group == SS.GW.activeGroup && nref->group != SS.GW.activeGroup) {
nref ->group.v != SS.GW.activeGroup.v)
{
// nfree is free, and nref is locked (since it came from a // nfree is free, and nref is locked (since it came from a
// previous group); so let's force nfree aligned to nref, // previous group); so let's force nfree aligned to nref,
// and make convergence easy // and make convergence easy
@ -746,7 +744,7 @@ void Constraint::MenuConstrain(Command id) {
} }
for(const Constraint &cc : SK.constraint) { for(const Constraint &cc : SK.constraint) {
if(c.h.v != cc.h.v && c.Equals(cc)) { if(c.h != cc.h && c.Equals(cc)) {
// Oops, we already have this exact constraint. Remove the one we just added. // Oops, we already have this exact constraint. Remove the one we just added.
SK.constraint.RemoveById(c.h); SK.constraint.RemoveById(c.h);
SS.GW.ClearSelection(); SS.GW.ClearSelection();

View File

@ -40,7 +40,7 @@ Expr *ConstraintBase::PointLineDistance(hEntity wrkpl, hEntity hpt, hEntity hln)
EntityBase *p = SK.GetEntity(hpt); EntityBase *p = SK.GetEntity(hpt);
if(wrkpl.v == EntityBase::FREE_IN_3D.v) { if(wrkpl == EntityBase::FREE_IN_3D) {
ExprVector ep = p->PointGetExprs(); ExprVector ep = p->PointGetExprs();
ExprVector ea = a->PointGetExprs(); ExprVector ea = a->PointGetExprs();
@ -82,7 +82,7 @@ Expr *ConstraintBase::Distance(hEntity wrkpl, hEntity hpa, hEntity hpb) {
ssassert(pa->IsPoint() && pb->IsPoint(), ssassert(pa->IsPoint() && pb->IsPoint(),
"Expected two points to measure projected distance between"); "Expected two points to measure projected distance between");
if(wrkpl.v == EntityBase::FREE_IN_3D.v) { if(wrkpl == EntityBase::FREE_IN_3D) {
// This is true distance // This is true distance
ExprVector ea, eb, eab; ExprVector ea, eb, eab;
ea = pa->PointGetExprs(); ea = pa->PointGetExprs();
@ -111,7 +111,7 @@ Expr *ConstraintBase::Distance(hEntity wrkpl, hEntity hpa, hEntity hpb) {
Expr *ConstraintBase::DirectionCosine(hEntity wrkpl, Expr *ConstraintBase::DirectionCosine(hEntity wrkpl,
ExprVector ae, ExprVector be) ExprVector ae, ExprVector be)
{ {
if(wrkpl.v == EntityBase::FREE_IN_3D.v) { if(wrkpl == EntityBase::FREE_IN_3D) {
Expr *mags = (ae.Magnitude())->Times(be.Magnitude()); Expr *mags = (ae.Magnitude())->Times(be.Magnitude());
return (ae.Dot(be))->Div(mags); return (ae.Dot(be))->Div(mags);
} else { } else {
@ -146,7 +146,7 @@ void ConstraintBase::ModifyToSatisfy() {
Vector a = SK.GetEntity(entityA)->VectorGetNum(); Vector a = SK.GetEntity(entityA)->VectorGetNum();
Vector b = SK.GetEntity(entityB)->VectorGetNum(); Vector b = SK.GetEntity(entityB)->VectorGetNum();
if(other) a = a.ScaledBy(-1); if(other) a = a.ScaledBy(-1);
if(workplane.v != EntityBase::FREE_IN_3D.v) { if(workplane != EntityBase::FREE_IN_3D) {
a = a.ProjectVectorInto(workplane); a = a.ProjectVectorInto(workplane);
b = b.ProjectVectorInto(workplane); b = b.ProjectVectorInto(workplane);
} }
@ -172,7 +172,7 @@ void ConstraintBase::ModifyToSatisfy() {
// These equations are written in the form f(...) - d = 0, where // These equations are written in the form f(...) - d = 0, where
// d is the value of the valA. // d is the value of the valA.
valA += (l.elem[0].e)->Eval(); valA += (l[0].e)->Eval();
l.Clear(); l.Clear();
} }
@ -190,7 +190,7 @@ void ConstraintBase::AddEq(IdList<Equation,hEquation> *l, const ExprVector &v,
int baseIndex) const { int baseIndex) const {
AddEq(l, v.x, baseIndex); AddEq(l, v.x, baseIndex);
AddEq(l, v.y, baseIndex + 1); AddEq(l, v.y, baseIndex + 1);
if(workplane.v == EntityBase::FREE_IN_3D.v) { if(workplane == EntityBase::FREE_IN_3D) {
AddEq(l, v.z, baseIndex + 2); AddEq(l, v.z, baseIndex + 2);
} }
} }
@ -200,7 +200,7 @@ void ConstraintBase::Generate(IdList<Param,hParam> *l) {
case Type::PARALLEL: case Type::PARALLEL:
case Type::CUBIC_LINE_TANGENT: case Type::CUBIC_LINE_TANGENT:
// Add new parameter only when we operate in 3d space // Add new parameter only when we operate in 3d space
if(workplane.v != EntityBase::FREE_IN_3D.v) break; if(workplane != EntityBase::FREE_IN_3D) break;
// fallthrough // fallthrough
case Type::SAME_ORIENTATION: case Type::SAME_ORIENTATION:
case Type::PT_ON_LINE: { case Type::PT_ON_LINE: {
@ -361,7 +361,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
case Type::POINTS_COINCIDENT: { case Type::POINTS_COINCIDENT: {
EntityBase *a = SK.GetEntity(ptA); EntityBase *a = SK.GetEntity(ptA);
EntityBase *b = SK.GetEntity(ptB); EntityBase *b = SK.GetEntity(ptB);
if(workplane.v == EntityBase::FREE_IN_3D.v) { if(workplane == EntityBase::FREE_IN_3D) {
ExprVector pa = a->PointGetExprs(); ExprVector pa = a->PointGetExprs();
ExprVector pb = b->PointGetExprs(); ExprVector pb = b->PointGetExprs();
AddEq(l, pa.x->Minus(pb.x), 0); AddEq(l, pa.x->Minus(pb.x), 0);
@ -430,7 +430,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
} }
case Type::AT_MIDPOINT: case Type::AT_MIDPOINT:
if(workplane.v == EntityBase::FREE_IN_3D.v) { if(workplane == EntityBase::FREE_IN_3D) {
EntityBase *ln = SK.GetEntity(entityA); EntityBase *ln = SK.GetEntity(entityA);
ExprVector a = SK.GetEntity(ln->point[0])->PointGetExprs(); ExprVector a = SK.GetEntity(ln->point[0])->PointGetExprs();
ExprVector b = SK.GetEntity(ln->point[1])->PointGetExprs(); ExprVector b = SK.GetEntity(ln->point[1])->PointGetExprs();
@ -469,7 +469,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
return; return;
case Type::SYMMETRIC: case Type::SYMMETRIC:
if(workplane.v == EntityBase::FREE_IN_3D.v) { if(workplane == EntityBase::FREE_IN_3D) {
EntityBase *plane = SK.GetEntity(entityA); EntityBase *plane = SK.GetEntity(entityA);
EntityBase *ea = SK.GetEntity(ptA); EntityBase *ea = SK.GetEntity(ptA);
EntityBase *eb = SK.GetEntity(ptB); EntityBase *eb = SK.GetEntity(ptB);
@ -521,7 +521,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
case Type::SYMMETRIC_HORIZ: case Type::SYMMETRIC_HORIZ:
case Type::SYMMETRIC_VERT: { case Type::SYMMETRIC_VERT: {
ssassert(workplane.v != Entity::FREE_IN_3D.v, ssassert(workplane != Entity::FREE_IN_3D,
"Unexpected horizontal/vertical symmetric constraint in 3d"); "Unexpected horizontal/vertical symmetric constraint in 3d");
EntityBase *a = SK.GetEntity(ptA); EntityBase *a = SK.GetEntity(ptA);
@ -576,7 +576,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
case Type::HORIZONTAL: case Type::HORIZONTAL:
case Type::VERTICAL: { case Type::VERTICAL: {
ssassert(workplane.v != Entity::FREE_IN_3D.v, ssassert(workplane != Entity::FREE_IN_3D,
"Unexpected horizontal/vertical constraint in 3d"); "Unexpected horizontal/vertical constraint in 3d");
hEntity ha, hb; hEntity ha, hb;
@ -701,7 +701,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
ExprVector b = line->VectorGetExprs(); ExprVector b = line->VectorGetExprs();
if(workplane.v == EntityBase::FREE_IN_3D.v) { if(workplane == EntityBase::FREE_IN_3D) {
ExprVector eq = VectorsParallel3d(a, b, valP); ExprVector eq = VectorsParallel3d(a, b, valP);
AddEq(l, eq); AddEq(l, eq);
} else { } else {
@ -755,7 +755,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
ExprVector a = ea->VectorGetExprsInWorkplane(workplane); ExprVector a = ea->VectorGetExprsInWorkplane(workplane);
ExprVector b = eb->VectorGetExprsInWorkplane(workplane); ExprVector b = eb->VectorGetExprsInWorkplane(workplane);
if(workplane.v == EntityBase::FREE_IN_3D.v) { if(workplane == EntityBase::FREE_IN_3D) {
ExprVector eq = VectorsParallel3d(a, b, valP); ExprVector eq = VectorsParallel3d(a, b, valP);
AddEq(l, eq); AddEq(l, eq);
} else { } else {
@ -775,7 +775,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
case Type::WHERE_DRAGGED: { case Type::WHERE_DRAGGED: {
EntityBase *ep = SK.GetEntity(ptA); EntityBase *ep = SK.GetEntity(ptA);
if(workplane.v == EntityBase::FREE_IN_3D.v) { if(workplane == EntityBase::FREE_IN_3D) {
ExprVector ev = ep->PointGetExprs(); ExprVector ev = ep->PointGetExprs();
Vector v = ep->PointGetNum(); Vector v = ep->PointGetNum();

View File

@ -35,7 +35,7 @@ void TextWindow::ScreenSetTtfFont(int link, uint32_t v) {
if(!r) return; if(!r) return;
SS.UndoRemember(); SS.UndoRemember();
r->font = SS.fonts.l.elem[i].FontFileBaseName(); r->font = SS.fonts.l[i].FontFileBaseName();
SS.MarkGroupDirty(r->group); SS.MarkGroupDirty(r->group);
SS.ScheduleShowTW(); SS.ScheduleShowTW();
} }
@ -175,9 +175,9 @@ void TextWindow::DescribeSelection() {
e->str.c_str(), &ScreenEditTtfText, e->h.request().v); e->str.c_str(), &ScreenEditTtfText, e->h.request().v);
Printf(true, " select new font"); Printf(true, " select new font");
SS.fonts.LoadAll(); SS.fonts.LoadAll();
int i; // Not using range-for here because we use i inside the output.
for(i = 0; i < SS.fonts.l.n; i++) { for(int i = 0; i < SS.fonts.l.n; i++) {
TtfFont *tf = &(SS.fonts.l.elem[i]); TtfFont *tf = &(SS.fonts.l[i]);
if(e->font == tf->FontFileBaseName()) { if(e->font == tf->FontFileBaseName()) {
Printf(false, "%Bp %s", Printf(false, "%Bp %s",
(i & 1) ? 'd' : 'a', (i & 1) ? 'd' : 'a',
@ -214,7 +214,7 @@ void TextWindow::DescribeSelection() {
Group *g = SK.GetGroup(e->group); Group *g = SK.GetGroup(e->group);
Printf(false, ""); Printf(false, "");
Printf(false, "%FtIN GROUP%E %s", g->DescriptionString().c_str()); Printf(false, "%FtIN GROUP%E %s", g->DescriptionString().c_str());
if(e->workplane.v == Entity::FREE_IN_3D.v) { if(e->workplane == Entity::FREE_IN_3D) {
Printf(false, "%FtNOT LOCKED IN WORKPLANE%E"); Printf(false, "%FtNOT LOCKED IN WORKPLANE%E");
} else { } else {
Entity *w = SK.GetEntity(e->workplane); Entity *w = SK.GetEntity(e->workplane);
@ -232,12 +232,13 @@ void TextWindow::DescribeSelection() {
std::vector<hConstraint> lhc = {}; std::vector<hConstraint> lhc = {};
for(const Constraint &c : SK.constraint) { for(const Constraint &c : SK.constraint) {
if(!(c.ptA.v == e->h.v || if(!(c.ptA == e->h ||
c.ptB.v == e->h.v || c.ptB == e->h ||
c.entityA.v == e->h.v || c.entityA == e->h ||
c.entityB.v == e->h.v || c.entityB == e->h ||
c.entityC.v == e->h.v || c.entityC == e->h ||
c.entityD.v == e->h.v)) continue; c.entityD == e->h))
continue;
lhc.push_back(c.h); lhc.push_back(c.h);
} }
@ -314,8 +315,7 @@ void TextWindow::DescribeSelection() {
Printf(true, " pt-ln distance = %Fi%s%E", Printf(true, " pt-ln distance = %Fi%s%E",
SS.MmToString(pp.DistanceToLine(lp0, lp1.Minus(lp0))).c_str()); SS.MmToString(pp.DistanceToLine(lp0, lp1.Minus(lp0))).c_str());
hEntity wrkpl = SS.GW.ActiveWorkplane(); hEntity wrkpl = SS.GW.ActiveWorkplane();
if(wrkpl.v != Entity::FREE_IN_3D.v && if(wrkpl != Entity::FREE_IN_3D && !(p->workplane == wrkpl && ln->workplane == wrkpl)) {
!(p->workplane.v == wrkpl.v && ln->workplane.v == wrkpl.v)) {
Vector ppw = pp.ProjectInto(wrkpl); Vector ppw = pp.ProjectInto(wrkpl);
Vector lp0w = lp0.ProjectInto(wrkpl); Vector lp0w = lp0.ProjectInto(wrkpl);
Vector lp1w = lp1.ProjectInto(wrkpl); Vector lp1w = lp1.ProjectInto(wrkpl);
@ -385,10 +385,9 @@ void TextWindow::DescribeSelection() {
lhe.push_back(c->entityC); lhe.push_back(c->entityC);
lhe.push_back(c->entityD); lhe.push_back(c->entityD);
auto it = std::remove_if(lhe.begin(), lhe.end(), auto it = std::remove_if(lhe.begin(), lhe.end(), [](hEntity he) {
[](hEntity he) { return he == Entity::NO_ENTITY || !he.isFromRequest();
return he.v == Entity::NO_ENTITY.v || !he.isFromRequest(); });
});
lhe.erase(it, lhe.end()); lhe.erase(it, lhe.end());
if(!lhe.empty()) { if(!lhe.empty()) {

View File

@ -8,8 +8,8 @@
#include "solvespace.h" #include "solvespace.h"
bool GraphicsWindow::Selection::Equals(Selection *b) { bool GraphicsWindow::Selection::Equals(Selection *b) {
if(entity.v != b->entity.v) return false; if(entity != b->entity) return false;
if(constraint.v != b->constraint.v) return false; if(constraint != b->constraint) return false;
return true; return true;
} }
@ -151,7 +151,8 @@ void GraphicsWindow::MakeUnselected(Selection *stog, bool coincidentPointTrick){
Vector ep = e->PointGetNum(); Vector ep = e->PointGetNum();
for(s = selection.First(); s; s = selection.NextAfter(s)) { for(s = selection.First(); s; s = selection.NextAfter(s)) {
if(!s->entity.v) continue; if(!s->entity.v) continue;
if(s->entity.v == stog->entity.v) continue; if(s->entity == stog->entity)
continue;
Entity *se = SK.GetEntity(s->entity); Entity *se = SK.GetEntity(s->entity);
if(!se->IsPoint()) continue; if(!se->IsPoint()) continue;
if(ep.Equals(se->PointGetNum())) { if(ep.Equals(se->PointGetNum())) {
@ -211,7 +212,7 @@ void GraphicsWindow::SelectByMarquee() {
Entity *e; Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->group.v != SS.GW.activeGroup.v) continue; if(e->group != SS.GW.activeGroup) continue;
if(e->IsFace() || e->IsDistance()) continue; if(e->IsFace() || e->IsDistance()) continue;
if(!e->IsVisible()) continue; if(!e->IsVisible()) continue;
@ -232,7 +233,7 @@ void GraphicsWindow::GroupSelection() {
gs = {}; gs = {};
int i; int i;
for(i = 0; i < selection.n; i++) { for(i = 0; i < selection.n; i++) {
Selection *s = &(selection.elem[i]); Selection *s = &(selection[i]);
if(s->entity.v) { if(s->entity.v) {
(gs.n)++; (gs.n)++;
@ -328,7 +329,8 @@ Lighting GraphicsWindow::GetLighting() const {
GraphicsWindow::Selection GraphicsWindow::ChooseFromHoverToSelect() { GraphicsWindow::Selection GraphicsWindow::ChooseFromHoverToSelect() {
Selection sel = {}; Selection sel = {};
if(hoverList.n == 0) return sel; if(hoverList.IsEmpty())
return sel;
Group *activeGroup = SK.GetGroup(SS.GW.activeGroup); Group *activeGroup = SK.GetGroup(SS.GW.activeGroup);
int bestOrder = -1; int bestOrder = -1;
@ -701,7 +703,7 @@ void GraphicsWindow::Draw(Canvas *canvas) {
if(SS.showContourAreas) { if(SS.showContourAreas) {
for(hGroup hg : SK.groupOrder) { for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(hg); Group *g = SK.GetGroup(hg);
if(g->h.v != activeGroup.v) continue; if(g->h != activeGroup) continue;
if(!(g->IsVisible())) continue; if(!(g->IsVisible())) continue;
g->DrawContourAreaLabels(canvas); g->DrawContourAreaLabels(canvas);
} }

View File

@ -148,7 +148,7 @@ int Constraint::DoLineTrimmedAgainstBox(Canvas *canvas, Canvas::hStroke hcs,
} }
if(j < 4) continue; if(j < 4) continue;
double t = (p.Minus(a)).DivPivoting(dl); double t = (p.Minus(a)).DivProjected(dl);
tmin = min(t, tmin); tmin = min(t, tmin);
tmax = max(t, tmax); tmax = max(t, tmax);
} }
@ -298,7 +298,7 @@ void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
Vector gr = camera.projRight.ScaledBy(1.0); Vector gr = camera.projRight.ScaledBy(1.0);
Vector gu = camera.projUp.ScaledBy(1.0); Vector gu = camera.projUp.ScaledBy(1.0);
if(workplane.v != Entity::FREE_IN_3D.v) { if(workplane != Entity::FREE_IN_3D) {
a0 = a0.ProjectInto(workplane); a0 = a0.ProjectInto(workplane);
b0 = b0.ProjectInto(workplane); b0 = b0.ProjectInto(workplane);
da = da.ProjectVectorInto(workplane); da = da.ProjectVectorInto(workplane);
@ -452,7 +452,7 @@ bool Constraint::IsVisible() const {
if(!(g->visible)) return false; if(!(g->visible)) return false;
// And likewise if the group is not the active group; except for comments // And likewise if the group is not the active group; except for comments
// with an assigned style. // with an assigned style.
if(g->h.v != SS.GW.activeGroup.v && !(type == Type::COMMENT && disp.style.v)) { if(g->h != SS.GW.activeGroup && !(type == Type::COMMENT && disp.style.v)) {
return false; return false;
} }
if(disp.style.v) { if(disp.style.v) {
@ -529,7 +529,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Vector ap = SK.GetEntity(ptA)->PointGetNum(); Vector ap = SK.GetEntity(ptA)->PointGetNum();
Vector bp = SK.GetEntity(ptB)->PointGetNum(); Vector bp = SK.GetEntity(ptB)->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) { if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &ap); DoProjectedPoint(canvas, hcs, &ap);
DoProjectedPoint(canvas, hcs, &bp); DoProjectedPoint(canvas, hcs, &bp);
} }
@ -596,7 +596,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Vector lB = SK.GetEntity(line->point[1])->PointGetNum(); Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
Vector dl = lB.Minus(lA); Vector dl = lB.Minus(lA);
if(workplane.v != Entity::FREE_IN_3D.v) { if(workplane != Entity::FREE_IN_3D) {
lA = lA.ProjectInto(workplane); lA = lA.ProjectInto(workplane);
lB = lB.ProjectInto(workplane); lB = lB.ProjectInto(workplane);
DoProjectedPoint(canvas, hcs, &pt); DoProjectedPoint(canvas, hcs, &pt);
@ -638,11 +638,11 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
} }
} }
if(workplane.v != Entity::FREE_IN_3D.v) { if(workplane != Entity::FREE_IN_3D) {
// Draw the projection marker from the closest point on the // Draw the projection marker from the closest point on the
// projected line to the projected point on the real line. // projected line to the projected point on the real line.
Vector lAB = (lA.Minus(lB)); Vector lAB = (lA.Minus(lB));
double t = (lA.Minus(closest)).DivPivoting(lAB); double t = (lA.Minus(closest)).DivProjected(lAB);
Vector lA = SK.GetEntity(line->point[0])->PointGetNum(); Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
Vector lB = SK.GetEntity(line->point[1])->PointGetNum(); Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
@ -829,7 +829,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
case Type::PERPENDICULAR: { case Type::PERPENDICULAR: {
Vector u = Vector::From(0, 0, 0), v = Vector::From(0, 0, 0); Vector u = Vector::From(0, 0, 0), v = Vector::From(0, 0, 0);
Vector rn, ru; Vector rn, ru;
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane == Entity::FREE_IN_3D) {
rn = gn; rn = gn;
ru = gu; ru = gu;
} else { } else {
@ -882,7 +882,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
v = norm->NormalV(); v = norm->NormalV();
} else if(type == Type::CUBIC_LINE_TANGENT) { } else if(type == Type::CUBIC_LINE_TANGENT) {
Vector n; Vector n;
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane == Entity::FREE_IN_3D) {
u = gr; u = gr;
v = gu; v = gu;
n = gn; n = gn;
@ -985,7 +985,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
a = SK.GetEntity(e->point[0])->PointGetNum(); a = SK.GetEntity(e->point[0])->PointGetNum();
b = SK.GetEntity(e->point[1])->PointGetNum(); b = SK.GetEntity(e->point[1])->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) { if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &a); DoProjectedPoint(canvas, hcs, &a);
DoProjectedPoint(canvas, hcs, &b); DoProjectedPoint(canvas, hcs, &b);
} }
@ -1005,7 +1005,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Entity *forLen = SK.GetEntity(entityA); Entity *forLen = SK.GetEntity(entityA);
Vector a = SK.GetEntity(forLen->point[0])->PointGetNum(), Vector a = SK.GetEntity(forLen->point[0])->PointGetNum(),
b = SK.GetEntity(forLen->point[1])->PointGetNum(); b = SK.GetEntity(forLen->point[1])->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) { if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &a); DoProjectedPoint(canvas, hcs, &a);
DoProjectedPoint(canvas, hcs, &b); DoProjectedPoint(canvas, hcs, &b);
} }
@ -1017,7 +1017,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Vector la = SK.GetEntity(ln->point[0])->PointGetNum(), Vector la = SK.GetEntity(ln->point[0])->PointGetNum(),
lb = SK.GetEntity(ln->point[1])->PointGetNum(); lb = SK.GetEntity(ln->point[1])->PointGetNum();
Vector pt = SK.GetEntity(ptA)->PointGetNum(); Vector pt = SK.GetEntity(ptA)->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) { if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &pt); DoProjectedPoint(canvas, hcs, &pt);
la = la.ProjectInto(workplane); la = la.ProjectInto(workplane);
lb = lb.ProjectInto(workplane); lb = lb.ProjectInto(workplane);
@ -1039,7 +1039,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Entity *pte = SK.GetEntity(i == 0 ? ptA : ptB); Entity *pte = SK.GetEntity(i == 0 ? ptA : ptB);
Vector pt = pte->PointGetNum(); Vector pt = pte->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) { if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &pt); DoProjectedPoint(canvas, hcs, &pt);
la = la.ProjectInto(workplane); la = la.ProjectInto(workplane);
lb = lb.ProjectInto(workplane); lb = lb.ProjectInto(workplane);
@ -1104,7 +1104,7 @@ s:
case Type::VERTICAL: case Type::VERTICAL:
if(entityA.v) { if(entityA.v) {
Vector r, u, n; Vector r, u, n;
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane == Entity::FREE_IN_3D) {
r = gr; u = gu; n = gn; r = gr; u = gu; n = gn;
} else { } else {
r = SK.GetEntity(workplane)->Normal()->NormalU(); r = SK.GetEntity(workplane)->Normal()->NormalU();
@ -1171,7 +1171,7 @@ s:
case Type::COMMENT: { case Type::COMMENT: {
Vector u, v; Vector u, v;
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane == Entity::FREE_IN_3D) {
u = gr; u = gr;
v = gu; v = gu;
} else { } else {

View File

@ -20,32 +20,31 @@ std::string Entity::DescriptionString() const {
void Entity::GenerateEdges(SEdgeList *el) { void Entity::GenerateEdges(SEdgeList *el) {
SBezierList *sbl = GetOrGenerateBezierCurves(); SBezierList *sbl = GetOrGenerateBezierCurves();
int i, j; for(int i = 0; i < sbl->l.n; i++) {
for(i = 0; i < sbl->l.n; i++) { SBezier *sb = &(sbl->l[i]);
SBezier *sb = &(sbl->l.elem[i]);
List<Vector> lv = {}; List<Vector> lv = {};
sb->MakePwlInto(&lv); sb->MakePwlInto(&lv);
for(j = 1; j < lv.n; j++) { for(int j = 1; j < lv.n; j++) {
el->AddEdge(lv.elem[j-1], lv.elem[j], style.v, i); el->AddEdge(lv[j-1], lv[j], style.v, i);
} }
lv.Clear(); lv.Clear();
} }
} }
SBezierList *Entity::GetOrGenerateBezierCurves() { SBezierList *Entity::GetOrGenerateBezierCurves() {
if(beziers.l.n == 0) if(beziers.l.IsEmpty())
GenerateBezierCurves(&beziers); GenerateBezierCurves(&beziers);
return &beziers; return &beziers;
} }
SEdgeList *Entity::GetOrGenerateEdges() { SEdgeList *Entity::GetOrGenerateEdges() {
if(edges.l.n != 0) { if(!edges.l.IsEmpty()) {
if(EXACT(edgesChordTol == SS.ChordTolMm())) if(EXACT(edgesChordTol == SS.ChordTolMm()))
return &edges; return &edges;
edges.l.Clear(); edges.l.Clear();
} }
if(edges.l.n == 0) if(edges.l.IsEmpty())
GenerateEdges(&edges); GenerateEdges(&edges);
edgesChordTol = SS.ChordTolMm(); edgesChordTol = SS.ChordTolMm();
return &edges; return &edges;
@ -55,7 +54,7 @@ BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) {
SBezierList *sbl = GetOrGenerateBezierCurves(); SBezierList *sbl = GetOrGenerateBezierCurves();
// We don't bother with bounding boxes for workplanes, etc. // We don't bother with bounding boxes for workplanes, etc.
*hasBBox = (IsPoint() || IsNormal() || sbl->l.n > 0); *hasBBox = (IsPoint() || IsNormal() || !sbl->l.IsEmpty());
if(!*hasBBox) return {}; if(!*hasBBox) return {};
if(screenBBoxValid) if(screenBBoxValid)
@ -67,16 +66,14 @@ BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) {
} else if(IsNormal()) { } else if(IsNormal()) {
Vector proj = SK.GetEntity(point[0])->PointGetNum(); Vector proj = SK.GetEntity(point[0])->PointGetNum();
screenBBox = BBox::From(proj, proj); screenBBox = BBox::From(proj, proj);
} else if(sbl->l.n > 0) { } else if(!sbl->l.IsEmpty()) {
Vector first = SS.GW.ProjectPoint3(sbl->l.elem[0].ctrl[0]); Vector first = SS.GW.ProjectPoint3(sbl->l[0].ctrl[0]);
screenBBox = BBox::From(first, first); screenBBox = BBox::From(first, first);
for(int i = 0; i < sbl->l.n; i++) { for(auto &sb : sbl->l) {
SBezier *sb = &sbl->l.elem[i]; for(int i = 0; i < sb.deg; ++i) { screenBBox.Include(SS.GW.ProjectPoint3(sb.ctrl[i])); }
for(int i = 0; i <= sb->deg; i++) {
screenBBox.Include(SS.GW.ProjectPoint3(sb->ctrl[i]));
}
} }
} else ssassert(false, "Expected entity to be a point or have beziers"); } else
ssassert(false, "Expected entity to be a point or have beziers");
screenBBoxValid = true; screenBBoxValid = true;
return screenBBox; return screenBBox;
@ -88,6 +85,7 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
case Type::POINT_N_TRANS: case Type::POINT_N_TRANS:
case Type::POINT_N_ROT_TRANS: case Type::POINT_N_ROT_TRANS:
case Type::POINT_N_ROT_AA: case Type::POINT_N_ROT_AA:
case Type::POINT_N_ROT_AXIS_TRANS:
case Type::POINT_IN_3D: case Type::POINT_IN_3D:
case Type::POINT_IN_2D: case Type::POINT_IN_2D:
refs->push_back(PointGetNum()); refs->push_back(PointGetNum());
@ -150,7 +148,7 @@ bool Entity::IsStylable() const {
bool Entity::IsVisible() const { bool Entity::IsVisible() const {
Group *g = SK.GetGroup(group); Group *g = SK.GetGroup(group);
if(g->h.v == Group::HGROUP_REFERENCES.v && IsNormal()) { if(g->h == Group::HGROUP_REFERENCES && IsNormal()) {
// The reference normals are always shown // The reference normals are always shown
return true; return true;
} }
@ -162,7 +160,7 @@ bool Entity::IsVisible() const {
if(!SS.GW.showWorkplanes) { if(!SS.GW.showWorkplanes) {
if(IsWorkplane() && !h.isFromRequest()) { if(IsWorkplane() && !h.isFromRequest()) {
if(g->h.v != SS.GW.activeGroup.v) { if(g->h != SS.GW.activeGroup) {
// The group-associated workplanes are hidden outside // The group-associated workplanes are hidden outside
// their group. // their group.
return false; return false;
@ -440,7 +438,7 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) const {
// Record our style for all of the Beziers that we just created. // Record our style for all of the Beziers that we just created.
for(; i < sbl->l.n; i++) { for(; i < sbl->l.n; i++) {
sbl->l.elem[i].auxA = style.v; sbl->l[i].auxA = style.v;
} }
} }
@ -453,7 +451,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
zIndex = 5; zIndex = 5;
} else if(how == DrawAs::HIDDEN) { } else if(how == DrawAs::HIDDEN) {
zIndex = 2; zIndex = 2;
} else if(group.v != SS.GW.activeGroup.v) { } else if(group != SS.GW.activeGroup) {
zIndex = 3; zIndex = 3;
} else { } else {
zIndex = 4; zIndex = 4;
@ -502,6 +500,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
case Type::POINT_N_TRANS: case Type::POINT_N_TRANS:
case Type::POINT_N_ROT_TRANS: case Type::POINT_N_ROT_TRANS:
case Type::POINT_N_ROT_AA: case Type::POINT_N_ROT_AA:
case Type::POINT_N_ROT_AXIS_TRANS:
case Type::POINT_IN_3D: case Type::POINT_IN_3D:
case Type::POINT_IN_2D: { case Type::POINT_IN_2D: {
if(how == DrawAs::HIDDEN) return; if(how == DrawAs::HIDDEN) return;
@ -568,11 +567,11 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
// dimmer for the ones at the model origin. // dimmer for the ones at the model origin.
hRequest hr = h.request(); hRequest hr = h.request();
uint8_t luma = (asReference) ? 255 : 100; uint8_t luma = (asReference) ? 255 : 100;
if(hr.v == Request::HREQUEST_REFERENCE_XY.v) { if(hr == Request::HREQUEST_REFERENCE_XY) {
stroke.color = RgbaColor::From(0, 0, luma); stroke.color = RgbaColor::From(0, 0, luma);
} else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) { } else if(hr == Request::HREQUEST_REFERENCE_YZ) {
stroke.color = RgbaColor::From(luma, 0, 0); stroke.color = RgbaColor::From(luma, 0, 0);
} else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) { } else if(hr == Request::HREQUEST_REFERENCE_ZX) {
stroke.color = RgbaColor::From(0, luma, 0); stroke.color = RgbaColor::From(0, luma, 0);
} }
} }
@ -587,6 +586,11 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
double s = camera.scale; double s = camera.scale;
double h = 60 - camera.height / 2.0; double h = 60 - camera.height / 2.0;
double w = 60 - camera.width / 2.0; double w = 60 - camera.width / 2.0;
// Shift the axis to the right if they would overlap with the toolbar.
if(SS.showToolbar) {
if(h + 30 > -(34*16 + 3*16 + 8) / 2)
w += 60;
}
tail = camera.projRight.ScaledBy(w/s).Plus( tail = camera.projRight.ScaledBy(w/s).Plus(
camera.projUp. ScaledBy(h/s)).Minus(camera.offset); camera.projUp. ScaledBy(h/s)).Minus(camera.offset);
} else { } else {

279
src/dsc.h
View File

@ -9,6 +9,34 @@
#include "solvespace.h" #include "solvespace.h"
#include <type_traits>
/// Trait indicating which types are handle types and should get the associated operators.
/// Specialize for each handle type and inherit from std::true_type.
template<typename T>
struct IsHandleOracle : std::false_type {};
// Equality-compare any two instances of a handle type.
template<typename T>
static inline typename std::enable_if<IsHandleOracle<T>::value, bool>::type
operator==(T const &lhs, T const &rhs) {
return lhs.v == rhs.v;
}
// Inequality-compare any two instances of a handle type.
template<typename T>
static inline typename std::enable_if<IsHandleOracle<T>::value, bool>::type
operator!=(T const &lhs, T const &rhs) {
return !(lhs == rhs);
}
// Less-than-compare any two instances of a handle type.
template<typename T>
static inline typename std::enable_if<IsHandleOracle<T>::value, bool>::type
operator<(T const &lhs, T const &rhs) {
return lhs.v < rhs.v;
}
class Vector; class Vector;
class Vector4; class Vector4;
class Point2d; class Point2d;
@ -92,7 +120,7 @@ public:
Vector ScaledBy(double s) const; Vector ScaledBy(double s) const;
Vector ProjectInto(hEntity wrkpl) const; Vector ProjectInto(hEntity wrkpl) const;
Vector ProjectVectorInto(hEntity wrkpl) const; Vector ProjectVectorInto(hEntity wrkpl) const;
double DivPivoting(Vector delta) const; double DivProjected(Vector delta) const;
Vector ClosestOrtho() const; Vector ClosestOrtho() const;
void MakeMaxMin(Vector *maxv, Vector *minv) const; void MakeMaxMin(Vector *maxv, Vector *minv) const;
Vector ClampWithin(double minv, double maxv) const; Vector ClampWithin(double minv, double maxv) const;
@ -159,7 +187,7 @@ public:
Point2d Plus(const Point2d &b) const; Point2d Plus(const Point2d &b) const;
Point2d Minus(const Point2d &b) const; Point2d Minus(const Point2d &b) const;
Point2d ScaledBy(double s) const; Point2d ScaledBy(double s) const;
double DivPivoting(Point2d delta) const; double DivProjected(Point2d delta) const;
double Dot(Point2d p) const; double Dot(Point2d p) const;
double DistanceTo(const Point2d &p) const; double DistanceTo(const Point2d &p) const;
double DistanceToLine(const Point2d &p0, const Point2d &dp, bool asSegment) const; double DistanceToLine(const Point2d &p0, const Point2d &dp, bool asSegment) const;
@ -174,17 +202,20 @@ public:
}; };
// A simple list // A simple list
template <class T> template<class T>
class List { class List {
T *elem = nullptr;
int elemsAllocated = 0;
public: public:
T *elem; int n = 0;
int n;
int elemsAllocated; bool IsEmpty() const { return n == 0; }
void ReserveMore(int howMuch) { void ReserveMore(int howMuch) {
if(n + howMuch > elemsAllocated) { if(n + howMuch > elemsAllocated) {
elemsAllocated = n + howMuch; elemsAllocated = n + howMuch;
T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(elem[0])); T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(T));
for(int i = 0; i < n; i++) { for(int i = 0; i < n; i++) {
new(&newElem[i]) T(std::move(elem[i])); new(&newElem[i]) T(std::move(elem[i]));
elem[i].~T(); elem[i].~T();
@ -214,31 +245,41 @@ public:
} }
T *First() { T *First() {
return (n == 0) ? NULL : &(elem[0]); return IsEmpty() ? nullptr : &(elem[0]);
} }
const T *First() const { const T *First() const {
return (n == 0) ? NULL : &(elem[0]); return IsEmpty() ? nullptr : &(elem[0]);
} }
T *Last() { return IsEmpty() ? nullptr : &(elem[n - 1]); }
const T *Last() const { return IsEmpty() ? nullptr : &(elem[n - 1]); }
T *NextAfter(T *prev) { T *NextAfter(T *prev) {
if(!prev) return NULL; if(IsEmpty() || !prev) return NULL;
if(prev - elem == (n - 1)) return NULL; if(prev - First() == (n - 1)) return NULL;
return prev + 1; return prev + 1;
} }
const T *NextAfter(const T *prev) const { const T *NextAfter(const T *prev) const {
if(!prev) return NULL; if(IsEmpty() || !prev) return NULL;
if(prev - elem == (n - 1)) return NULL; if(prev - First() == (n - 1)) return NULL;
return prev + 1; return prev + 1;
} }
T *begin() { return &elem[0]; } T &Get(size_t i) { return elem[i]; }
T *end() { return &elem[n]; } T const &Get(size_t i) const { return elem[i]; }
const T *begin() const { return &elem[0]; } T &operator[](size_t i) { return Get(i); }
const T *end() const { return &elem[n]; } T const &operator[](size_t i) const { return Get(i); }
T *begin() { return IsEmpty() ? nullptr : &elem[0]; }
T *end() { return IsEmpty() ? nullptr : &elem[n]; }
const T *begin() const { return IsEmpty() ? nullptr : &elem[0]; }
const T *end() const { return IsEmpty() ? nullptr : &elem[n]; }
const T *cbegin() const { return begin(); }
const T *cend() const { return end(); }
void ClearTags() { void ClearTags() {
int i; for(auto & elt : *this) {
for(i = 0; i < n; i++) { elt.tag = 0;
elem[i].tag = 0;
} }
} }
@ -251,21 +292,20 @@ public:
} }
void RemoveTagged() { void RemoveTagged() {
int src, dest; auto newEnd = std::remove_if(this->begin(), this->end(), [](T &t) {
dest = 0; if(t.tag) {
for(src = 0; src < n; src++) { return true;
if(elem[src].tag) { }
// this item should be deleted return false;
} else { });
if(src != dest) { auto oldEnd = this->end();
elem[dest] = elem[src]; n = newEnd - begin();
} if (newEnd != nullptr && oldEnd != nullptr) {
dest++; while(newEnd != oldEnd) {
newEnd->~T();
++newEnd;
} }
} }
for(int i = dest; i < n; i++)
elem[i].~T();
n = dest;
// and elemsAllocated is untouched, because we didn't resize // and elemsAllocated is untouched, because we didn't resize
} }
@ -285,21 +325,44 @@ public:
} }
}; };
// Comparison functor used by IdList and related classes
template <class T, class H>
struct CompareId {
bool operator()(T const& lhs, T const& rhs) const {
return lhs.h.v < rhs.h.v;
}
bool operator()(T const& lhs, H rhs) const {
return lhs.h.v < rhs.v;
}
};
// A list, where each element has an integer identifier. The list is kept // A list, where each element has an integer identifier. The list is kept
// sorted by that identifier, and items can be looked up in log n time by // sorted by that identifier, and items can be looked up in log n time by
// id. // id.
template <class T, class H> template <class T, class H>
class IdList { class IdList {
T *elem = nullptr;
int elemsAllocated = 0;
public: public:
T *elem; int n = 0;
int n;
int elemsAllocated; using Compare = CompareId<T, H>;
bool IsEmpty() const {
return n == 0;
}
void AllocForOneMore() {
if(n >= elemsAllocated) {
ReserveMore((elemsAllocated + 32)*2 - n);
}
}
uint32_t MaximumId() { uint32_t MaximumId() {
if(n == 0) { if(IsEmpty()) {
return 0; return 0;
} else { } else {
return elem[n - 1].h.v; return Last()->h.v;
} }
} }
@ -310,10 +373,35 @@ public:
return t->h; return t->h;
} }
T * LowerBound(T const& t) {
if(IsEmpty()) {
return nullptr;
}
auto it = std::lower_bound(begin(), end(), t, Compare());
return it;
}
T * LowerBound(H const& h) {
if(IsEmpty()) {
return nullptr;
}
auto it = std::lower_bound(begin(), end(), h, Compare());
return it;
}
int LowerBoundIndex(T const& t) {
if(IsEmpty()) {
return 0;
}
auto it = LowerBound(t);
auto idx = std::distance(begin(), it);
auto i = static_cast<int>(idx);
return i;
}
void ReserveMore(int howMuch) { void ReserveMore(int howMuch) {
if(n + howMuch > elemsAllocated) { if(n + howMuch > elemsAllocated) {
elemsAllocated = n + howMuch; elemsAllocated = n + howMuch;
T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(elem[0])); T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(T));
for(int i = 0; i < n; i++) { for(int i = 0; i < n; i++) {
new(&newElem[i]) T(std::move(elem[i])); new(&newElem[i]) T(std::move(elem[i]));
elem[i].~T(); elem[i].~T();
@ -324,28 +412,16 @@ public:
} }
void Add(T *t) { void Add(T *t) {
if(n >= elemsAllocated) { AllocForOneMore();
ReserveMore((elemsAllocated + 32)*2 - n);
}
int first = 0, last = n; // Look to see if we already have something with the same handle value.
// We know that we must insert within the closed interval [first,last] ssassert(FindByIdNoOops(t->h) == nullptr, "Handle isn't unique");
while(first != last) {
int mid = (first + last)/2;
H hm = elem[mid].h;
ssassert(hm.v != t->h.v, "Handle isn't unique");
if(hm.v > t->h.v) {
last = mid;
} else if(hm.v < t->h.v) {
first = mid + 1;
}
}
int i = first; // Copy-construct at the end of the list.
new(&elem[n]) T(); new(&elem[n]) T(*t);
std::move_backward(elem + i, elem + n, elem + n + 1); ++n;
elem[i] = *t; // The item we just added is trivially sorted, so "merge"
n++; std::inplace_merge(begin(), end() - 1, end(), Compare());
} }
T *FindById(H h) { T *FindById(H h) {
@ -355,50 +431,54 @@ public:
} }
int IndexOf(H h) { int IndexOf(H h) {
int first = 0, last = n-1; if(IsEmpty()) {
while(first <= last) { return -1;
int mid = (first + last)/2; }
H hm = elem[mid].h; auto it = LowerBound(h);
if(hm.v > h.v) { auto idx = std::distance(begin(), it);
last = mid-1; // and first stays the same if (idx < n) {
} else if(hm.v < h.v) { return idx;
first = mid+1; // and last stays the same
} else {
return mid;
}
} }
return -1; return -1;
} }
T *FindByIdNoOops(H h) { T *FindByIdNoOops(H h) {
int first = 0, last = n-1; if(IsEmpty()) {
while(first <= last) { return nullptr;
int mid = (first + last)/2;
H hm = elem[mid].h;
if(hm.v > h.v) {
last = mid-1; // and first stays the same
} else if(hm.v < h.v) {
first = mid+1; // and last stays the same
} else {
return &(elem[mid]);
}
} }
return NULL; auto it = LowerBound(h);
if (it == nullptr || it == end()) {
return nullptr;
}
if (it->h.v == h.v) {
return it;
}
return nullptr;
} }
T *First() { T *First() {
return (n == 0) ? NULL : &(elem[0]); return (IsEmpty()) ? NULL : &(elem[0]);
}
T *Last() {
return (IsEmpty()) ? NULL : &(elem[n-1]);
} }
T *NextAfter(T *prev) { T *NextAfter(T *prev) {
if(!prev) return NULL; if(IsEmpty() || !prev) return NULL;
if(prev - elem == (n - 1)) return NULL; if(prev - First() == (n - 1)) return NULL;
return prev + 1; return prev + 1;
} }
T *begin() { return &elem[0]; } T &Get(size_t i) { return elem[i]; }
T *end() { return &elem[n]; } T const &Get(size_t i) const { return elem[i]; }
const T *begin() const { return &elem[0]; } T &operator[](size_t i) { return Get(i); }
const T *end() const { return &elem[n]; } T const &operator[](size_t i) const { return Get(i); }
T *begin() { return IsEmpty() ? nullptr : &elem[0]; }
T *end() { return IsEmpty() ? nullptr : &elem[0] + n; }
const T *begin() const { return IsEmpty() ? nullptr : &elem[0]; }
const T *end() const { return IsEmpty() ? nullptr : &elem[0] + n; }
const T *cbegin() const { return begin(); }
const T *cend() const { return end(); }
template<typename F> template<typename F>
size_t CountIf(F &&predicate) const { size_t CountIf(F &&predicate) const {
@ -406,18 +486,13 @@ public:
} }
void ClearTags() { void ClearTags() {
int i; for(auto &elt : *this) { elt.tag = 0; }
for(i = 0; i < n; i++) {
elem[i].tag = 0;
}
} }
void Tag(H h, int tag) { void Tag(H h, int tag) {
int i; auto it = FindByIdNoOops(h);
for(i = 0; i < n; i++) { if (it != nullptr) {
if(elem[i].h.v == h.v) { it->tag = tag;
elem[i].tag = tag;
}
} }
} }
@ -448,9 +523,9 @@ public:
void MoveSelfInto(IdList<T,H> *l) { void MoveSelfInto(IdList<T,H> *l) {
l->Clear(); l->Clear();
*l = *this; std::swap(l->elem, elem);
elemsAllocated = n = 0; std::swap(l->elemsAllocated, elemsAllocated);
elem = NULL; std::swap(l->n, n);
} }
void DeepCopyInto(IdList<T,H> *l) { void DeepCopyInto(IdList<T,H> *l) {
@ -467,9 +542,9 @@ public:
elem[i].Clear(); elem[i].Clear();
elem[i].~T(); elem[i].~T();
} }
elemsAllocated = n = 0;
if(elem) MemFree(elem); if(elem) MemFree(elem);
elem = NULL; elem = NULL;
elemsAllocated = n = 0;
} }
}; };

View File

@ -37,7 +37,7 @@ ExprVector EntityBase::VectorGetExprsInWorkplane(hEntity wrkpl) const {
case Type::NORMAL_N_ROT: case Type::NORMAL_N_ROT:
case Type::NORMAL_N_ROT_AA: { case Type::NORMAL_N_ROT_AA: {
ExprVector ev = NormalExprsN(); ExprVector ev = NormalExprsN();
if(wrkpl.v == EntityBase::FREE_IN_3D.v) { if(wrkpl == EntityBase::FREE_IN_3D) {
return ev; return ev;
} }
// Get the offset and basis vectors for this weird exotic csys. // Get the offset and basis vectors for this weird exotic csys.
@ -245,6 +245,7 @@ bool EntityBase::IsPoint() const {
case Type::POINT_N_TRANS: case Type::POINT_N_TRANS:
case Type::POINT_N_ROT_TRANS: case Type::POINT_N_ROT_TRANS:
case Type::POINT_N_ROT_AA: case Type::POINT_N_ROT_AA:
case Type::POINT_N_ROT_AXIS_TRANS:
return true; return true;
default: default:
@ -454,10 +455,38 @@ void EntityBase::PointForceTo(Vector p) {
// in order to avoid jumps when you cross from +pi to -pi // in order to avoid jumps when you cross from +pi to -pi
while(dtheta < -PI) dtheta += 2*PI; while(dtheta < -PI) dtheta += 2*PI;
while(dtheta > PI) dtheta -= 2*PI; while(dtheta > PI) dtheta -= 2*PI;
// this extra *2 explains the mystery *4
SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2); SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2);
break; break;
} }
case Type::POINT_N_ROT_AXIS_TRANS: {
if(timesApplied == 0) break;
// is the point on the rotation axis?
Vector offset = Vector::From(param[0], param[1], param[2]);
Vector normal = Vector::From(param[4], param[5], param[6]).WithMagnitude(1.0);
Vector check = numPoint.Minus(offset).Cross(normal);
if (check.Dot(check) < LENGTH_EPS) { // if so, do extrusion style drag
Vector trans = (p.Minus(numPoint));
SK.GetParam(param[7])->val = trans.Dot(normal)/timesApplied;
} else { // otherwise do rotation style
Vector u = normal.Normal(0), v = normal.Normal(1);
Vector po = p.Minus(offset), numo = numPoint.Minus(offset);
double thetap = atan2(v.Dot(po), u.Dot(po));
double thetan = atan2(v.Dot(numo), u.Dot(numo));
double thetaf = (thetap - thetan);
double thetai = (SK.GetParam(param[3])->val)*timesApplied*2;
double dtheta = thetaf - thetai;
// Take the smallest possible change in the actual step angle,
// in order to avoid jumps when you cross from +pi to -pi
while(dtheta < -PI) dtheta += 2*PI;
while(dtheta > PI) dtheta -= 2*PI;
// this extra *2 explains the mystery *4
SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2);
}
break;
}
case Type::POINT_N_COPY: case Type::POINT_N_COPY:
// Nothing to do; it's a static copy // Nothing to do; it's a static copy
break; break;
@ -506,6 +535,17 @@ Vector EntityBase::PointGetNum() const {
break; break;
} }
case Type::POINT_N_ROT_AXIS_TRANS: {
Vector offset = Vector::From(param[0], param[1], param[2]);
Vector displace = Vector::From(param[4], param[5], param[6])
.WithMagnitude(SK.GetParam(param[7])->val).ScaledBy(timesApplied);
Quaternion q = PointGetQuaternion();
p = numPoint.Minus(offset);
p = q.Rotate(p);
p = p.Plus(offset).Plus(displace);
break;
}
case Type::POINT_N_COPY: case Type::POINT_N_COPY:
p = numPoint; p = numPoint;
break; break;
@ -555,6 +595,18 @@ ExprVector EntityBase::PointGetExprs() const {
r = orig.Plus(trans); r = orig.Plus(trans);
break; break;
} }
case Type::POINT_N_ROT_AXIS_TRANS: {
ExprVector orig = ExprVector::From(numPoint);
ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
ExprVector displace = ExprVector::From(param[4], param[5], param[6])
.WithMagnitude(Expr::From(1.0)).ScaledBy(Expr::From(timesApplied)).ScaledBy(Expr::From(param[7]));
ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
orig = orig.Minus(trans);
orig = q.Rotate(orig);
r = orig.Plus(trans).Plus(displace);
break;
}
case Type::POINT_N_COPY: case Type::POINT_N_COPY:
r = ExprVector::From(numPoint); r = ExprVector::From(numPoint);
break; break;
@ -565,7 +617,7 @@ ExprVector EntityBase::PointGetExprs() const {
} }
void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) const { void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) const {
if(type == Type::POINT_IN_2D && workplane.v == wrkpl.v) { if(type == Type::POINT_IN_2D && workplane == wrkpl) {
// They want our coordinates in the form that we've written them, // They want our coordinates in the form that we've written them,
// very nice. // very nice.
*u = Expr::From(param[0]); *u = Expr::From(param[0]);
@ -587,7 +639,7 @@ void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) con
} }
ExprVector EntityBase::PointGetExprsInWorkplane(hEntity wrkpl) const { ExprVector EntityBase::PointGetExprsInWorkplane(hEntity wrkpl) const {
if(wrkpl.v == Entity::FREE_IN_3D.v) { if(wrkpl == Entity::FREE_IN_3D) {
return PointGetExprs(); return PointGetExprs();
} }
@ -633,7 +685,7 @@ ExprQuaternion EntityBase::GetAxisAngleQuaternionExprs(int param0) const {
Quaternion EntityBase::PointGetQuaternion() const { Quaternion EntityBase::PointGetQuaternion() const {
Quaternion q; Quaternion q;
if(type == Type::POINT_N_ROT_AA) { if(type == Type::POINT_N_ROT_AA || type == Type::POINT_N_ROT_AXIS_TRANS) {
q = GetAxisAngleQuaternion(3); q = GetAxisAngleQuaternion(3);
} else if(type == Type::POINT_N_ROT_TRANS) { } else if(type == Type::POINT_N_ROT_TRANS) {
q = Quaternion::From(param[3], param[4], param[5], param[6]); q = Quaternion::From(param[3], param[4], param[5], param[6]);
@ -807,7 +859,7 @@ bool EntityBase::IsInPlane(Vector norm, double distance) const {
case Type::CIRCLE: case Type::CIRCLE:
case Type::ARC_OF_CIRCLE: { case Type::ARC_OF_CIRCLE: {
// If it is an (arc of) a circle, check whether the normals // If it is an (arc of) a circle, check whether the normals
// are parallel and the mid point is in the plane. // are parallel and the mid point is in the plane.
Vector n = Normal()->NormalN(); Vector n = Normal()->NormalN();
if (!norm.Equals(n) && !norm.Equals(n.Negated())) return false; if (!norm.Equals(n) && !norm.Equals(n.Negated())) return false;
@ -870,19 +922,16 @@ void EntityBase::GenerateEquations(IdList<Equation,hEquation> *l) const {
// If the two endpoints of the arc are constrained coincident // If the two endpoints of the arc are constrained coincident
// (to make a complete circle), then our distance constraint // (to make a complete circle), then our distance constraint
// would be redundant and therefore overconstrain things. // would be redundant and therefore overconstrain things.
int i; auto it = std::find_if(SK.constraint.begin(), SK.constraint.end(),
for(i = 0; i < SK.constraint.n; i++) { [&](ConstraintBase const &con) {
ConstraintBase *c = &(SK.constraint.elem[i]); return (con.group == group) &&
if(c->group.v != group.v) continue; (con.type == Constraint::Type::POINTS_COINCIDENT) &&
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue; ((con.ptA == point[1] && con.ptB == point[2]) ||
(con.ptA == point[2] && con.ptB == point[1]));
if((c->ptA.v == point[1].v && c->ptB.v == point[2].v) || });
(c->ptA.v == point[2].v && c->ptB.v == point[1].v)) if(it != SK.constraint.end()) {
{ break;
break;
}
} }
if(i < SK.constraint.n) break;
Expr *ra = Constraint::Distance(workplane, point[0], point[1]); Expr *ra = Constraint::Distance(workplane, point[0], point[1]);
Expr *rb = Constraint::Distance(workplane, point[0], point[2]); Expr *rb = Constraint::Distance(workplane, point[0], point[2]);

View File

@ -28,7 +28,7 @@ void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) {
SS.GW.GroupSelection(); SS.GW.GroupSelection();
auto const &gs = SS.GW.gs; auto const &gs = SS.GW.gs;
if((gs.n == 0 && g->activeWorkplane.v != Entity::FREE_IN_3D.v)) { if((gs.n == 0 && g->activeWorkplane != Entity::FREE_IN_3D)) {
Entity *wrkpl = SK.GetEntity(g->activeWorkplane); Entity *wrkpl = SK.GetEntity(g->activeWorkplane);
origin = wrkpl->WorkplaneGetOffset(); origin = wrkpl->WorkplaneGetOffset();
n = wrkpl->Normal()->NormalN(); n = wrkpl->Normal()->NormalN();
@ -94,11 +94,10 @@ void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) {
// Remove all overlapping edges/beziers to merge the areas they describe. // Remove all overlapping edges/beziers to merge the areas they describe.
el.CullExtraneousEdges(/*both=*/true); el.CullExtraneousEdges(/*both=*/true);
bl.CullIdenticalBeziers(/*both=*/true); bl.CullIdenticalBeziers(/*both=*/true);
// Collect lines and beziers with custom style & export. // Collect lines and beziers with custom style & export.
int i; for(auto &ent : SK.entity) {
for(i = 0; i < SK.entity.n; i++) { Entity *e = &ent;
Entity *e = &(SK.entity.elem[i]);
if (!e->IsVisible()) continue; if (!e->IsVisible()) continue;
if (e->style.v < Style::FIRST_CUSTOM) continue; if (e->style.v < Style::FIRST_CUSTOM) continue;
if (!Style::Exportable(e->style.v)) continue; if (!Style::Exportable(e->style.v)) continue;
@ -186,7 +185,6 @@ public:
}; };
void SolveSpaceUI::ExportViewOrWireframeTo(const Platform::Path &filename, bool exportWireframe) { void SolveSpaceUI::ExportViewOrWireframeTo(const Platform::Path &filename, bool exportWireframe) {
int i;
SEdgeList edges = {}; SEdgeList edges = {};
SBezierList beziers = {}; SBezierList beziers = {};
@ -206,8 +204,8 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const Platform::Path &filename, bool
sm = NULL; sm = NULL;
} }
for(i = 0; i < SK.entity.n; i++) { for(auto &entity : SK.entity) {
Entity *e = &(SK.entity.elem[i]); Entity *e = &entity;
if(!e->IsVisible()) continue; if(!e->IsVisible()) continue;
if(e->construction) continue; if(e->construction) continue;
@ -321,16 +319,14 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
// Project into the export plane; so when we're done, z doesn't matter, // Project into the export plane; so when we're done, z doesn't matter,
// and x and y are what goes in the DXF. // and x and y are what goes in the DXF.
SEdge *e; for(SEdge *e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
// project into the specified csys, and apply export scale // project into the specified csys, and apply export scale
(e->a) = e->a.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); (e->a) = e->a.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
(e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); (e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
} }
SBezier *b;
if(sbl) { if(sbl) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) { for(SBezier *b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
*b = b->InPerspective(u, v, n, origin, cameraTan); *b = b->InPerspective(u, v, n, origin, cameraTan);
int i; int i;
for(i = 0; i <= b->deg; i++) { for(i = 0; i <= b->deg; i++) {
@ -459,7 +455,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
// segments with zero-length projections. // segments with zero-length projections.
sel->l.ClearTags(); sel->l.ClearTags();
for(int i = 0; i < sel->l.n; ++i) { for(int i = 0; i < sel->l.n; ++i) {
SEdge *sei = &sel->l.elem[i]; SEdge *sei = &sel->l[i];
hStyle hsi = { (uint32_t)sei->auxA }; hStyle hsi = { (uint32_t)sei->auxA };
Style *si = Style::Get(hsi); Style *si = Style::Get(hsi);
if(sei->tag != 0) continue; if(sei->tag != 0) continue;
@ -476,7 +472,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
} }
for(int j = i + 1; j < sel->l.n; ++j) { for(int j = i + 1; j < sel->l.n; ++j) {
SEdge *sej = &sel->l.elem[j]; SEdge *sej = &sel->l[j];
if(sej->tag != 0) continue; if(sej->tag != 0) continue;
Vector *pAj = &sej->a; Vector *pAj = &sej->a;
@ -578,12 +574,13 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
// We kept the line segments and Beziers separate until now; but put them // We kept the line segments and Beziers separate until now; but put them
// all together, and also project everything into the xy plane, since not // all together, and also project everything into the xy plane, since not
// all export targets ignore the z component of the points. // all export targets ignore the z component of the points.
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) { ssassert(sbl != nullptr, "Adding line segments to beziers assumes bezier list is non-null.");
for(SEdge *e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
SBezier sb = SBezier::From(e->a, e->b); SBezier sb = SBezier::From(e->a, e->b);
sb.auxA = e->auxA; sb.auxA = e->auxA;
sbl->l.Add(&sb); sbl->l.Add(&sb);
} }
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) { for(SBezier *b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
for(int i = 0; i <= b->deg; i++) { for(int i = 0; i <= b->deg; i++) {
b->ctrl[i].z = 0; b->ctrl[i].z = 0;
} }
@ -762,9 +759,9 @@ void VectorFileWriter::OutputLinesAndMesh(SBezierLoopSetSet *sblss, SMesh *sm) {
void VectorFileWriter::BezierAsPwl(SBezier *sb) { void VectorFileWriter::BezierAsPwl(SBezier *sb) {
List<Vector> lv = {}; List<Vector> lv = {};
sb->MakePwlInto(&lv, SS.ExportChordTolMm()); sb->MakePwlInto(&lv, SS.ExportChordTolMm());
int i;
for(i = 1; i < lv.n; i++) { for(int i = 1; i < lv.n; i++) {
SBezier sb = SBezier::From(lv.elem[i-1], lv.elem[i]); SBezier sb = SBezier::From(lv[i-1], lv[i]);
Bezier(&sb); Bezier(&sb);
} }
lv.Clear(); lv.Clear();
@ -849,6 +846,8 @@ void SolveSpaceUI::ExportMeshTo(const Platform::Path &filename) {
filename.HasExtension("html")) { filename.HasExtension("html")) {
SOutlineList *e = &(SK.GetGroup(SS.GW.activeGroup)->displayOutlines); SOutlineList *e = &(SK.GetGroup(SS.GW.activeGroup)->displayOutlines);
ExportMeshAsThreeJsTo(f, filename, m, e); ExportMeshAsThreeJsTo(f, filename, m, e);
} else if(filename.HasExtension("wrl")) {
ExportMeshAsVrmlTo(f, filename, m);
} else { } else {
Error("Can't identify output file type from file extension of " Error("Can't identify output file type from file extension of "
"filename '%s'; try .stl, .obj, .js, .html.", filename.raw.c_str()); "filename '%s'; try .stl, .obj, .js, .html.", filename.raw.c_str());
@ -876,7 +875,7 @@ void SolveSpaceUI::ExportMeshAsStlTo(FILE *f, SMesh *sm) {
double s = SS.exportScale; double s = SS.exportScale;
int i; int i;
for(i = 0; i < sm->l.n; i++) { for(i = 0; i < sm->l.n; i++) {
STriangle *tr = &(sm->l.elem[i]); STriangle *tr = &(sm->l[i]);
Vector n = tr->Normal().WithMagnitude(1); Vector n = tr->Normal().WithMagnitude(1);
float w; float w;
w = (float)n.x; fwrite(&w, 4, 1, f); w = (float)n.x; fwrite(&w, 4, 1, f);
@ -900,7 +899,7 @@ void SolveSpaceUI::ExportMeshAsStlTo(FILE *f, SMesh *sm) {
// Export the mesh as a Q3DO (https://github.com/q3k/q3d) file. // Export the mesh as a Q3DO (https://github.com/q3k/q3d) file.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include "object_generated.h" #include "q3d_object_generated.h"
void SolveSpaceUI::ExportMeshAsQ3doTo(FILE *f, SMesh *sm) { void SolveSpaceUI::ExportMeshAsQ3doTo(FILE *f, SMesh *sm) {
flatbuffers::FlatBufferBuilder builder(1024); flatbuffers::FlatBufferBuilder builder(1024);
double s = SS.exportScale; double s = SS.exportScale;
@ -982,7 +981,7 @@ void SolveSpaceUI::ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm) {
RgbaColor currentColor = {}; RgbaColor currentColor = {};
for(int i = 0; i < sm->l.n; i++) { for(int i = 0; i < sm->l.n; i++) {
const STriangle &t = sm->l.elem[i]; const STriangle &t = sm->l[i];
if(!currentColor.Equals(t.meta.color)) { if(!currentColor.Equals(t.meta.color)) {
currentColor = t.meta.color; currentColor = t.meta.color;
fprintf(fObj, "usemtl %s\n", colors[currentColor].c_str()); fprintf(fObj, "usemtl %s\n", colors[currentColor].c_str());
@ -1168,6 +1167,139 @@ void SolveSpaceUI::ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename
spl.Clear(); spl.Clear();
} }
//-----------------------------------------------------------------------------
// Export the mesh as a VRML text file / WRL.
//-----------------------------------------------------------------------------
void SolveSpaceUI::ExportMeshAsVrmlTo(FILE *f, const Platform::Path &filename, SMesh *sm) {
struct STriangleSpan {
STriangle *first, *past_last;
STriangle *begin() const { return first; }
STriangle *end() const { return past_last; }
};
std::string basename = filename.FileStem();
for(auto & c : basename) {
if(!(isalnum(c) || ((unsigned)c >= 0x80))) {
c = '_';
}
}
fprintf(f, "#VRML V2.0 utf8\n"
"#Exported from SolveSpace %s\n"
"\n"
"DEF %s Transform {\n"
" children [",
PACKAGE_VERSION,
basename.c_str());
std::map<std::uint8_t, std::vector<STriangleSpan>> opacities;
STriangle *start = sm->l.begin();
std::uint8_t last_opacity = start->meta.color.alpha;
for(auto & tr : sm->l) {
if(tr.meta.color.alpha != last_opacity) {
opacities[last_opacity].push_back(STriangleSpan{start, &tr});
start = &tr;
last_opacity = start->meta.color.alpha;
}
}
opacities[last_opacity].push_back(STriangleSpan{start, sm->l.end()});
for(auto && op : opacities) {
fprintf(f, "\n"
" Shape {\n"
" appearance Appearance {\n"
" material DEF %s_material_%u Material {\n"
" diffuseColor %f %f %f\n"
" ambientIntensity %f\n"
" transparency %f\n"
" }\n"
" }\n"
" geometry IndexedFaceSet {\n"
" colorPerVertex TRUE\n"
" coord Coordinate { point [\n",
basename.c_str(),
(unsigned)op.first,
SS.ambientIntensity,
SS.ambientIntensity,
SS.ambientIntensity,
SS.ambientIntensity,
1.f - ((float)op.first / 255.0f));
SPointList spl = {};
for(const auto & sp : op.second) {
for(const auto & tr : sp) {
spl.IncrementTagFor(tr.a);
spl.IncrementTagFor(tr.b);
spl.IncrementTagFor(tr.c);
}
}
// Output all the vertices.
for(auto sp : spl.l) {
fprintf(f, " %f %f %f,\n",
sp.p.x / SS.exportScale,
sp.p.y / SS.exportScale,
sp.p.z / SS.exportScale);
}
fputs(" ] }\n"
" coordIndex [\n", f);
// And now all the triangular faces, in terms of those vertices.
for(const auto & sp : op.second) {
for(const auto & tr : sp) {
fprintf(f, " %d, %d, %d, -1,\n",
spl.IndexForPoint(tr.a),
spl.IndexForPoint(tr.b),
spl.IndexForPoint(tr.c));
}
}
fputs(" ]\n"
" color Color { color [\n", f);
// Output triangle colors.
std::vector<int> triangle_colour_ids;
std::vector<RgbaColor> colours_present;
for(const auto & sp : op.second) {
for(const auto & tr : sp) {
const auto colour_itr = std::find_if(colours_present.begin(), colours_present.end(),
[&](const RgbaColor & c) {
return c.Equals(tr.meta.color);
});
if(colour_itr == colours_present.end()) {
fprintf(f, " %.10f %.10f %.10f,\n",
tr.meta.color.redF(),
tr.meta.color.greenF(),
tr.meta.color.blueF());
triangle_colour_ids.push_back(colours_present.size());
colours_present.insert(colours_present.end(), tr.meta.color);
} else {
triangle_colour_ids.push_back(colour_itr - colours_present.begin());
}
}
}
fputs(" ] }\n"
" colorIndex [\n", f);
for(auto colour_idx : triangle_colour_ids) {
fprintf(f, " %d, %d, %d, -1,\n", colour_idx, colour_idx, colour_idx);
}
fputs(" ]\n"
" }\n"
" }\n", f);
spl.Clear();
}
fputs(" ]\n"
"}\n", f);
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Export a view of the model as an image; we just take a screenshot, by // Export a view of the model as an image; we just take a screenshot, by
// rendering the view in the usual way and then copying the pixels. // rendering the view in the usual way and then copying the pixels.

View File

@ -121,7 +121,7 @@ int StepFileWriter::ExportCurveLoop(SBezierLoop *loop, bool inner) {
List<int> listOfTrims = {}; List<int> listOfTrims = {};
SBezier *sb = &(loop->l.elem[loop->l.n - 1]); SBezier *sb = loop->l.Last();
// Generate "exactly closed" contours, with the same vertex id for the // Generate "exactly closed" contours, with the same vertex id for the
// finish of a previous edge and the start of the next one. So we need // finish of a previous edge and the start of the next one. So we need
@ -296,12 +296,13 @@ void StepFileWriter::ExportSurfacesTo(const Platform::Path &filename) {
Group *g = SK.GetGroup(SS.GW.activeGroup); Group *g = SK.GetGroup(SS.GW.activeGroup);
SShell *shell = &(g->runningShell); SShell *shell = &(g->runningShell);
if(shell->surface.n == 0) { if(shell->surface.IsEmpty()) {
Error("The model does not contain any surfaces to export.%s", Error("The model does not contain any surfaces to export.%s",
g->runningMesh.l.n > 0 ? !g->runningMesh.l.IsEmpty()
"\n\nThe model does contain triangles from a mesh, but " ? "\n\nThe model does contain triangles from a mesh, but "
"a triangle mesh cannot be exported as a STEP file. Try " "a triangle mesh cannot be exported as a STEP file. Try "
"File -> Export Mesh... instead." : ""); "File -> Export Mesh... instead."
: "");
return; return;
} }
@ -318,7 +319,8 @@ void StepFileWriter::ExportSurfacesTo(const Platform::Path &filename) {
SSurface *ss; SSurface *ss;
for(ss = shell->surface.First(); ss; ss = shell->surface.NextAfter(ss)) { for(ss = shell->surface.First(); ss; ss = shell->surface.NextAfter(ss)) {
if(ss->trim.n == 0) continue; if(ss->trim.IsEmpty())
continue;
// Get all of the loops of Beziers that trim our surface (with each // Get all of the loops of Beziers that trim our surface (with each
// Bezier split so that we use the section as t goes from 0 to 1), and // Bezier split so that we use the section as t goes from 0 to 1), and

View File

@ -325,7 +325,7 @@ public:
void assignEntityDefaults(DRW_Entity *entity, hStyle hs) { void assignEntityDefaults(DRW_Entity *entity, hStyle hs) {
Style *s = Style::Get(hs); Style *s = Style::Get(hs);
RgbaColor color = s->Color(hs, /*forExport=*/true); RgbaColor color = Style::Color(hs, /*forExport=*/true);
entity->color24 = color.ToPackedIntBGRA(); entity->color24 = color.ToPackedIntBGRA();
entity->color = findDxfColor(color); entity->color = findDxfColor(color);
entity->layer = s->DescriptionString(); entity->layer = s->DescriptionString();
@ -370,7 +370,7 @@ public:
DRW_Polyline polyline; DRW_Polyline polyline;
assignEntityDefaults(&polyline, hs); assignEntityDefaults(&polyline, hs);
for(int i = 0; i < lv.n; i++) { for(int i = 0; i < lv.n; i++) {
Vector *v = &lv.elem[i]; Vector *v = &lv[i];
DRW_Vertex *vertex = new DRW_Vertex(v->x, v->y, v->z, 0.0); DRW_Vertex *vertex = new DRW_Vertex(v->x, v->y, v->z, 0.0);
polyline.vertlist.push_back(vertex); polyline.vertlist.push_back(vertex);
} }
@ -1027,8 +1027,9 @@ void SvgFileWriter::StartFile() {
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000; double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
fprintf(f, "stroke-width:%f;\r\n", sw); fprintf(f, "stroke-width:%f;\r\n", sw);
fprintf(f, "}\r\n"); fprintf(f, "}\r\n");
for(int i = 0; i < SK.style.n; i++) { for(auto &style : SK.style) {
Style *s = &SK.style.elem[i]; Style *s = &style;
RgbaColor strokeRgb = Style::Color(s->h, /*forExport=*/true); RgbaColor strokeRgb = Style::Color(s->h, /*forExport=*/true);
StipplePattern pattern = Style::PatternType(s->h); StipplePattern pattern = Style::PatternType(s->h);
double stippleScale = Style::StippleScaleMm(s->h); double stippleScale = Style::StippleScaleMm(s->h);

View File

@ -361,8 +361,8 @@ Expr *Expr::PartialWrt(hParam p) const {
Expr *da, *db; Expr *da, *db;
switch(op) { switch(op) {
case Op::PARAM_PTR: return From(p.v == parp->h.v ? 1 : 0); case Op::PARAM_PTR: return From(p == parp->h ? 1 : 0);
case Op::PARAM: return From(p.v == parh.v ? 1 : 0); case Op::PARAM: return From(p == parh ? 1 : 0);
case Op::CONSTANT: return From(0.0); case Op::CONSTANT: return From(0.0);
case Op::VARIABLE: ssassert(false, "Not supported yet"); case Op::VARIABLE: ssassert(false, "Not supported yet");
@ -412,8 +412,8 @@ uint64_t Expr::ParamsUsed() const {
} }
bool Expr::DependsOn(hParam p) const { bool Expr::DependsOn(hParam p) const {
if(op == Op::PARAM) return (parh.v == p.v); if(op == Op::PARAM) return (parh == p);
if(op == Op::PARAM_PTR) return (parp->h.v == p.v); if(op == Op::PARAM_PTR) return (parp->h == p);
int c = Children(); int c = Children();
if(c == 1) return a->DependsOn(p); if(c == 1) return a->DependsOn(p);
@ -494,7 +494,7 @@ Expr *Expr::FoldConstants() {
void Expr::Substitute(hParam oldh, hParam newh) { void Expr::Substitute(hParam oldh, hParam newh) {
ssassert(op != Op::PARAM_PTR, "Expected an expression that refer to params via handles"); ssassert(op != Op::PARAM_PTR, "Expected an expression that refer to params via handles");
if(op == Op::PARAM && parh.v == oldh.v) { if(op == Op::PARAM && parh == oldh) {
parh = newh; parh = newh;
} }
int c = Children(); int c = Children();
@ -528,11 +528,11 @@ hParam Expr::ReferencedParams(ParamList *pl) const {
hParam pa, pb; hParam pa, pb;
pa = a->ReferencedParams(pl); pa = a->ReferencedParams(pl);
pb = b->ReferencedParams(pl); pb = b->ReferencedParams(pl);
if(pa.v == NO_PARAMS.v) { if(pa == NO_PARAMS) {
return pb; return pb;
} else if(pb.v == NO_PARAMS.v) { } else if(pb == NO_PARAMS) {
return pa; return pa;
} else if(pa.v == pb.v) { } else if(pa == pb) {
return pa; // either, doesn't matter return pa; // either, doesn't matter
} else { } else {
return MULTIPLE_PARAMS; return MULTIPLE_PARAMS;

View File

@ -45,7 +45,7 @@ public:
Expr *b; Expr *b;
}; };
Expr() { } Expr() = default;
Expr(double val) : op(Op::CONSTANT) { v = val; } Expr(double val) : op(Op::CONSTANT) { v = val; }
static inline Expr *AllocExpr() static inline Expr *AllocExpr()

View File

@ -20,8 +20,8 @@ void SolveSpaceUI::ClearExisting() {
UndoClearStack(&redo); UndoClearStack(&redo);
UndoClearStack(&undo); UndoClearStack(&undo);
for(int i = 0; i < SK.groupOrder.n; i++) { for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]); Group *g = SK.GetGroup(hg);
g->Clear(); g->Clear();
} }
@ -301,39 +301,39 @@ bool SolveSpaceUI::SaveToFile(const Platform::Path &filename) {
fprintf(fh, "%s\n\n\n", VERSION_STRING); fprintf(fh, "%s\n\n\n", VERSION_STRING);
int i, j; int i, j;
for(i = 0; i < SK.group.n; i++) { for(auto &g : SK.group) {
sv.g = SK.group.elem[i]; sv.g = g;
SaveUsingTable(filename, 'g'); SaveUsingTable(filename, 'g');
fprintf(fh, "AddGroup\n\n"); fprintf(fh, "AddGroup\n\n");
} }
for(i = 0; i < SK.param.n; i++) { for(auto &p : SK.param) {
sv.p = SK.param.elem[i]; sv.p = p;
SaveUsingTable(filename, 'p'); SaveUsingTable(filename, 'p');
fprintf(fh, "AddParam\n\n"); fprintf(fh, "AddParam\n\n");
} }
for(i = 0; i < SK.request.n; i++) { for(auto &r : SK.request) {
sv.r = SK.request.elem[i]; sv.r = r;
SaveUsingTable(filename, 'r'); SaveUsingTable(filename, 'r');
fprintf(fh, "AddRequest\n\n"); fprintf(fh, "AddRequest\n\n");
} }
for(i = 0; i < SK.entity.n; i++) { for(auto &e : SK.entity) {
(SK.entity.elem[i]).CalculateNumerical(/*forExport=*/true); e.CalculateNumerical(/*forExport=*/true);
sv.e = SK.entity.elem[i]; sv.e = e;
SaveUsingTable(filename, 'e'); SaveUsingTable(filename, 'e');
fprintf(fh, "AddEntity\n\n"); fprintf(fh, "AddEntity\n\n");
} }
for(i = 0; i < SK.constraint.n; i++) { for(auto &c : SK.constraint) {
sv.c = SK.constraint.elem[i]; sv.c = c;
SaveUsingTable(filename, 'c'); SaveUsingTable(filename, 'c');
fprintf(fh, "AddConstraint\n\n"); fprintf(fh, "AddConstraint\n\n");
} }
for(i = 0; i < SK.style.n; i++) { for(auto &s : SK.style) {
sv.s = SK.style.elem[i]; sv.s = s;
if(sv.s.h.v >= Style::FIRST_CUSTOM) { if(sv.s.h.v >= Style::FIRST_CUSTOM) {
SaveUsingTable(filename, 's'); SaveUsingTable(filename, 's');
fprintf(fh, "AddStyle\n\n"); fprintf(fh, "AddStyle\n\n");
@ -343,10 +343,10 @@ bool SolveSpaceUI::SaveToFile(const Platform::Path &filename) {
// A group will have either a mesh or a shell, but not both; but the code // A group will have either a mesh or a shell, but not both; but the code
// to print either of those just does nothing if the mesh/shell is empty. // to print either of those just does nothing if the mesh/shell is empty.
Group *g = SK.GetGroup(SK.groupOrder.elem[SK.groupOrder.n - 1]); Group *g = SK.GetGroup(*SK.groupOrder.Last());
SMesh *m = &g->runningMesh; SMesh *m = &g->runningMesh;
for(i = 0; i < m->l.n; i++) { for(i = 0; i < m->l.n; i++) {
STriangle *tr = &(m->l.elem[i]); STriangle *tr = &(m->l[i]);
fprintf(fh, "Triangle %08x %08x " fprintf(fh, "Triangle %08x %08x "
"%.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f\n", "%.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f\n",
tr->meta.face, tr->meta.color.ToPackedInt(), tr->meta.face, tr->meta.color.ToPackedInt(),
@ -436,8 +436,17 @@ void SolveSpaceUI::LoadUsingTable(const Platform::Path &filename, char *key, cha
if (fgets(line2, (int)sizeof(line2), fh) == NULL) if (fgets(line2, (int)sizeof(line2), fh) == NULL)
break; break;
if(sscanf(line2, "%d %x %d", &(ei.v), &(ek.input.v), if(sscanf(line2, "%d %x %d", &(ei.v), &(ek.input.v),
&(ek.copyNumber)) == 3) &(ek.copyNumber)) == 3) {
{ if(ei.v == Entity::NO_ENTITY.v) {
// Commit bd84bc1a mistakenly introduced code that would remap
// some entities to NO_ENTITY. This was fixed in commit bd84bc1a,
// but files created meanwhile are corrupt, and can cause crashes.
//
// To fix this, we skip any such remaps when loading; they will be
// recreated on the next regeneration. Any resulting orphans will
// be pruned in the usual way, recovering to a well-defined state.
continue;
}
p->M().insert({ ek, ei }); p->M().insert({ ek, ei });
} else { } else {
break; break;
@ -540,7 +549,7 @@ bool SolveSpaceUI::LoadFromFile(const Platform::Path &filename, bool canCancel)
Error(_("Unrecognized data in file. This file may be corrupt, or " Error(_("Unrecognized data in file. This file may be corrupt, or "
"from a newer version of the program.")); "from a newer version of the program."));
// At least leave the program in a non-crashing state. // At least leave the program in a non-crashing state.
if(SK.group.n == 0) { if(SK.group.IsEmpty()) {
NewFile(); NewFile();
} }
} }

View File

@ -14,11 +14,10 @@ void SolveSpaceUI::MarkGroupDirtyByEntity(hEntity he) {
} }
void SolveSpaceUI::MarkGroupDirty(hGroup hg, bool onlyThis) { void SolveSpaceUI::MarkGroupDirty(hGroup hg, bool onlyThis) {
int i;
bool go = false; bool go = false;
for(i = 0; i < SK.groupOrder.n; i++) { for(auto const &gh : SK.groupOrder) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]); Group *g = SK.GetGroup(gh);
if(g->h.v == hg.v) { if(g->h == hg) {
go = true; go = true;
} }
if(go) { if(go) {
@ -31,23 +30,20 @@ void SolveSpaceUI::MarkGroupDirty(hGroup hg, bool onlyThis) {
} }
bool SolveSpaceUI::PruneOrphans() { bool SolveSpaceUI::PruneOrphans() {
int i;
for(i = 0; i < SK.request.n; i++) {
Request *r = &(SK.request.elem[i]);
if(GroupExists(r->group)) continue;
auto r = std::find_if(SK.request.begin(), SK.request.end(),
[&](Request &r) { return !GroupExists(r.group); });
if(r != SK.request.end()) {
(deleted.requests)++; (deleted.requests)++;
SK.request.RemoveById(r->h); SK.request.RemoveById(r->h);
return true; return true;
} }
for(i = 0; i < SK.constraint.n; i++) { auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(),
Constraint *c = &(SK.constraint.elem[i]); [&](Constraint &c) { return !GroupExists(c.group); });
if(GroupExists(c->group)) continue; if(c != SK.constraint.end()) {
(deleted.constraints)++; (deleted.constraints)++;
(deleted.nonTrivialConstraints)++; (deleted.nonTrivialConstraints)++;
SK.constraint.RemoveById(c->h); SK.constraint.RemoveById(c->h);
return true; return true;
} }
@ -72,7 +68,7 @@ bool SolveSpaceUI::GroupExists(hGroup hg) {
bool SolveSpaceUI::EntityExists(hEntity he) { bool SolveSpaceUI::EntityExists(hEntity he) {
// A nonexstient entity is acceptable, though, usually just means it // A nonexstient entity is acceptable, though, usually just means it
// doesn't apply. // doesn't apply.
if(he.v == Entity::NO_ENTITY.v) return true; if(he == Entity::NO_ENTITY) return true;
return SK.entity.FindByIdNoOops(he) ? true : false; return SK.entity.FindByIdNoOops(he) ? true : false;
} }
@ -91,44 +87,38 @@ bool SolveSpaceUI::PruneGroups(hGroup hg) {
} }
bool SolveSpaceUI::PruneRequests(hGroup hg) { bool SolveSpaceUI::PruneRequests(hGroup hg) {
int i; auto e = std::find_if(SK.entity.begin(), SK.entity.end(),
for(i = 0; i < SK.entity.n; i++) { [&](Entity &e) { return e.group == hg && !EntityExists(e.workplane); });
Entity *e = &(SK.entity.elem[i]); if(e != SK.entity.end()) {
if(e->group.v != hg.v) continue;
if(EntityExists(e->workplane)) continue;
ssassert(e->h.isFromRequest(), "Only explicitly created entities can be pruned");
(deleted.requests)++; (deleted.requests)++;
SK.request.RemoveById(e->h.request()); SK.entity.RemoveById(e->h);
return true; return true;
} }
return false; return false;
} }
bool SolveSpaceUI::PruneConstraints(hGroup hg) { bool SolveSpaceUI::PruneConstraints(hGroup hg) {
int i; auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(), [&](Constraint &c) {
for(i = 0; i < SK.constraint.n; i++) { if(c.group != hg)
Constraint *c = &(SK.constraint.elem[i]); return false;
if(c->group.v != hg.v) continue;
if(EntityExists(c->workplane) && if(EntityExists(c.workplane) &&
EntityExists(c->ptA) && EntityExists(c.ptA) &&
EntityExists(c->ptB) && EntityExists(c.ptB) &&
EntityExists(c->entityA) && EntityExists(c.entityA) &&
EntityExists(c->entityB) && EntityExists(c.entityB) &&
EntityExists(c->entityC) && EntityExists(c.entityC) &&
EntityExists(c->entityD)) EntityExists(c.entityD)) {
{ return false;
continue;
} }
return true;
});
if(c != SK.constraint.end()) {
(deleted.constraints)++; (deleted.constraints)++;
if(c->type != Constraint::Type::POINTS_COINCIDENT && if(c->type != Constraint::Type::POINTS_COINCIDENT &&
c->type != Constraint::Type::HORIZONTAL && c->type != Constraint::Type::HORIZONTAL &&
c->type != Constraint::Type::VERTICAL) c->type != Constraint::Type::VERTICAL) {
{
(deleted.nonTrivialConstraints)++; (deleted.nonTrivialConstraints)++;
} }
@ -139,14 +129,13 @@ bool SolveSpaceUI::PruneConstraints(hGroup hg) {
} }
void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox) { void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox) {
int first = 0, last = 0, i, j; int first = 0, last = 0, i;
uint64_t startMillis = GetMilliseconds(), uint64_t startMillis = GetMilliseconds(),
endMillis; endMillis;
SK.groupOrder.Clear(); SK.groupOrder.Clear();
for(int i = 0; i < SK.group.n; i++) for(auto &g : SK.group) { SK.groupOrder.Add(&g.h); }
SK.groupOrder.Add(&SK.group.elem[i].h);
std::sort(SK.groupOrder.begin(), SK.groupOrder.end(), std::sort(SK.groupOrder.begin(), SK.groupOrder.end(),
[](const hGroup &ha, const hGroup &hb) { [](const hGroup &ha, const hGroup &hb) {
return SK.GetGroup(ha)->order < SK.GetGroup(hb)->order; return SK.GetGroup(ha)->order < SK.GetGroup(hb)->order;
@ -159,12 +148,13 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
// Start from the first dirty group, and solve until the active group, // Start from the first dirty group, and solve until the active group,
// since all groups after the active group are hidden. // since all groups after the active group are hidden.
// Not using range-for because we're tracking the indices.
for(i = 0; i < SK.groupOrder.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]); Group *g = SK.GetGroup(SK.groupOrder[i]);
if((!g->clean) || !g->IsSolvedOkay()) { if((!g->clean) || !g->IsSolvedOkay()) {
first = min(first, i); first = min(first, i);
} }
if(g->h.v == SS.GW.activeGroup.v) { if(g->h == SS.GW.activeGroup) {
last = i; last = i;
} }
} }
@ -190,7 +180,7 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
case Generate::UNTIL_ACTIVE: { case Generate::UNTIL_ACTIVE: {
for(i = 0; i < SK.groupOrder.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
if(SK.groupOrder.elem[i].v == SS.GW.activeGroup.v) if(SK.groupOrder[i] == SS.GW.activeGroup)
break; break;
} }
@ -224,8 +214,9 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
SK.entity.Clear(); SK.entity.Clear();
SK.entity.ReserveMore(oldEntityCount); SK.entity.ReserveMore(oldEntityCount);
// Not using range-for because we're using the index inside the loop.
for(i = 0; i < SK.groupOrder.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]); Group *g = SK.GetGroup(SK.groupOrder[i]);
// The group may depend on entities or other groups, to define its // The group may depend on entities or other groups, to define its
// workplane geometry or for its operands. Those must already exist // workplane geometry or for its operands. Those must already exist
@ -233,15 +224,15 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
if(PruneGroups(g->h)) if(PruneGroups(g->h))
goto pruned; goto pruned;
for(j = 0; j < SK.request.n; j++) { for(auto &req : SK.request) {
Request *r = &(SK.request.elem[j]); Request *r = &req;
if(r->group.v != g->h.v) continue; if(r->group != g->h) continue;
r->Generate(&(SK.entity), &(SK.param)); r->Generate(&(SK.entity), &(SK.param));
} }
for(j = 0; j < SK.constraint.n; j++) { for(auto &con : SK.constraint) {
Constraint *c = &SK.constraint.elem[j]; Constraint *c = &con;
if(c->group.v != g->h.v) continue; if(c->group != g->h) continue;
c->Generate(&(SK.param)); c->Generate(&(SK.param));
} }
@ -254,8 +245,8 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
// Use the previous values for params that we've seen before, as // Use the previous values for params that we've seen before, as
// initial guesses for the solver. // initial guesses for the solver.
for(j = 0; j < SK.param.n; j++) { for(auto &p : SK.param) {
Param *newp = &(SK.param.elem[j]); Param *newp = &p;
if(newp->known) continue; if(newp->known) continue;
Param *prevp = prev.FindByIdNoOops(newp->h); Param *prevp = prev.FindByIdNoOops(newp->h);
@ -265,11 +256,12 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
} }
} }
if(g->h.v == Group::HGROUP_REFERENCES.v) { if(g->h == Group::HGROUP_REFERENCES) {
ForceReferences(); ForceReferences();
g->solved.how = SolveResult::OKAY; g->solved.how = SolveResult::OKAY;
g->clean = true; g->clean = true;
} else { } else {
// this i is an index in groupOrder
if(i >= first && i <= last) { if(i >= first && i <= last) {
// The group falls inside the range, so really solve it, // The group falls inside the range, so really solve it,
// and then regenerate the mesh based on the solved stuff. // and then regenerate the mesh based on the solved stuff.
@ -284,8 +276,8 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
// The group falls outside the range, so just assume that // The group falls outside the range, so just assume that
// it's good wherever we left it. The mesh is unchanged, // it's good wherever we left it. The mesh is unchanged,
// and the parameters must be marked as known. // and the parameters must be marked as known.
for(j = 0; j < SK.param.n; j++) { for(auto &p : SK.param) {
Param *newp = &(SK.param.elem[j]); Param *newp = &p;
Param *prevp = prev.FindByIdNoOops(newp->h); Param *prevp = prev.FindByIdNoOops(newp->h);
if(prevp) newp->known = true; if(prevp) newp->known = true;
@ -295,8 +287,8 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
} }
// And update any reference dimensions with their new values // And update any reference dimensions with their new values
for(i = 0; i < SK.constraint.n; i++) { for(auto &con : SK.constraint) {
Constraint *c = &(SK.constraint.elem[i]); Constraint *c = &con;
if(c->reference) { if(c->reference) {
c->ModifyToSatisfy(); c->ModifyToSatisfy();
} }
@ -433,7 +425,7 @@ void SolveSpaceUI::MarkDraggedParams() {
if(i == -1) { if(i == -1) {
hp = SS.GW.pending.point; hp = SS.GW.pending.point;
} else { } else {
hp = SS.GW.pending.points.elem[i]; hp = SS.GW.pending.points[i];
} }
if(!hp.v) continue; if(!hp.v) continue;
@ -445,6 +437,7 @@ void SolveSpaceUI::MarkDraggedParams() {
switch(pt->type) { switch(pt->type) {
case Entity::Type::POINT_N_TRANS: case Entity::Type::POINT_N_TRANS:
case Entity::Type::POINT_IN_3D: case Entity::Type::POINT_IN_3D:
case Entity::Type::POINT_N_ROT_AXIS_TRANS:
sys.dragged.Add(&(pt->param[0])); sys.dragged.Add(&(pt->param[0]));
sys.dragged.Add(&(pt->param[1])); sys.dragged.Add(&(pt->param[1]));
sys.dragged.Add(&(pt->param[2])); sys.dragged.Add(&(pt->param[2]));
@ -504,21 +497,20 @@ void SolveSpaceUI::SolveGroupAndReport(hGroup hg, bool andFindFree) {
} }
void SolveSpaceUI::WriteEqSystemForGroup(hGroup hg) { void SolveSpaceUI::WriteEqSystemForGroup(hGroup hg) {
int i;
// Clear out the system to be solved. // Clear out the system to be solved.
sys.entity.Clear(); sys.entity.Clear();
sys.param.Clear(); sys.param.Clear();
sys.eq.Clear(); sys.eq.Clear();
// And generate all the params for requests in this group // And generate all the params for requests in this group
for(i = 0; i < SK.request.n; i++) { for(auto &req : SK.request) {
Request *r = &(SK.request.elem[i]); Request *r = &req;
if(r->group.v != hg.v) continue; if(r->group != hg) continue;
r->Generate(&(sys.entity), &(sys.param)); r->Generate(&(sys.entity), &(sys.param));
} }
for(i = 0; i < SK.constraint.n; i++) { for(auto &con : SK.constraint) {
Constraint *c = &SK.constraint.elem[i]; Constraint *c = &con;
if(c->group.v != hg.v) continue; if(c->group != hg) continue;
c->Generate(&(sys.param)); c->Generate(&(sys.param));
} }
@ -526,8 +518,8 @@ void SolveSpaceUI::WriteEqSystemForGroup(hGroup hg) {
Group *g = SK.GetGroup(hg); Group *g = SK.GetGroup(hg);
g->Generate(&(sys.entity), &(sys.param)); g->Generate(&(sys.entity), &(sys.param));
// Set the initial guesses for all the params // Set the initial guesses for all the params
for(i = 0; i < sys.param.n; i++) { for(auto &param : sys.param) {
Param *p = &(sys.param.elem[i]); Param *p = &param;
p->known = false; p->known = false;
p->val = SK.GetParam(p->h)->val; p->val = SK.GetParam(p->h)->val;
} }
@ -562,10 +554,10 @@ SolveResult SolveSpaceUI::TestRankForGroup(hGroup hg, int *rank) {
bool SolveSpaceUI::ActiveGroupsOkay() { bool SolveSpaceUI::ActiveGroupsOkay() {
for(int i = 0; i < SK.groupOrder.n; i++) { for(int i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]); Group *g = SK.GetGroup(SK.groupOrder[i]);
if(!g->IsSolvedOkay()) if(!g->IsSolvedOkay())
return false; return false;
if(g->h.v == SS.GW.activeGroup.v) if(g->h == SS.GW.activeGroup)
break; break;
} }
return true; return true;

View File

@ -105,7 +105,9 @@ const MenuEntry Menu[] = {
{ 1, N_("Step &Rotating"), Command::GROUP_ROT, S|'r', KN, mGrp }, { 1, N_("Step &Rotating"), Command::GROUP_ROT, S|'r', KN, mGrp },
{ 1, NULL, Command::NONE, 0, KN, NULL }, { 1, NULL, Command::NONE, 0, KN, NULL },
{ 1, N_("E&xtrude"), Command::GROUP_EXTRUDE, S|'x', KN, mGrp }, { 1, N_("E&xtrude"), Command::GROUP_EXTRUDE, S|'x', KN, mGrp },
{ 1, N_("&Helix"), Command::GROUP_HELIX, S|'h', KN, mGrp },
{ 1, N_("&Lathe"), Command::GROUP_LATHE, S|'l', KN, mGrp }, { 1, N_("&Lathe"), Command::GROUP_LATHE, S|'l', KN, mGrp },
{ 1, N_("Re&volve"), Command::GROUP_REVOLVE, S|'v', KN, mGrp },
{ 1, NULL, Command::NONE, 0, KN, NULL }, { 1, NULL, Command::NONE, 0, KN, NULL },
{ 1, N_("Link / Assemble..."), Command::GROUP_LINK, S|'i', KN, mGrp }, { 1, N_("Link / Assemble..."), Command::GROUP_LINK, S|'i', KN, mGrp },
{ 1, N_("Link Recent"), Command::GROUP_RECENT, 0, KN, mGrp }, { 1, N_("Link Recent"), Command::GROUP_RECENT, 0, KN, mGrp },
@ -379,7 +381,9 @@ void GraphicsWindow::Init() {
orig.projUp = projUp; orig.projUp = projUp;
// And with the last group active // And with the last group active
activeGroup = SK.groupOrder.elem[SK.groupOrder.n - 1]; ssassert(!SK.groupOrder.IsEmpty(),
"Group order can't be empty since we will activate the last group.");
activeGroup = *SK.groupOrder.Last();
SK.GetGroup(activeGroup)->Activate(); SK.GetGroup(activeGroup)->Activate();
showWorkplanes = false; showWorkplanes = false;
@ -571,7 +575,7 @@ void GraphicsWindow::LoopOverPoints(const std::vector<Entity *> &entities,
Group *g = SK.GetGroup(activeGroup); Group *g = SK.GetGroup(activeGroup);
g->GenerateDisplayItems(); g->GenerateDisplayItems();
for(int i = 0; i < g->displayMesh.l.n; i++) { for(int i = 0; i < g->displayMesh.l.n; i++) {
STriangle *tr = &(g->displayMesh.l.elem[i]); STriangle *tr = &(g->displayMesh.l[i]);
if(!includeMesh) { if(!includeMesh) {
bool found = false; bool found = false;
for(const hEntity &face : faces) { for(const hEntity &face : faces) {
@ -587,9 +591,9 @@ void GraphicsWindow::LoopOverPoints(const std::vector<Entity *> &entities,
} }
if(!includeMesh) return; if(!includeMesh) return;
for(int i = 0; i < g->polyLoops.l.n; i++) { for(int i = 0; i < g->polyLoops.l.n; i++) {
SContour *sc = &(g->polyLoops.l.elem[i]); SContour *sc = &(g->polyLoops.l[i]);
for(int j = 0; j < sc->l.n; j++) { for(int j = 0; j < sc->l.n; j++) {
HandlePointForZoomToFit(sc->l.elem[j].p, pmax, pmin, wmin, usePerspective, camera); HandlePointForZoomToFit(sc->l[j].p, pmax, pmin, wmin, usePerspective, camera);
} }
} }
} }
@ -606,7 +610,7 @@ double GraphicsWindow::ZoomToFit(const Camera &camera,
if(useSelection) { if(useSelection) {
for(int i = 0; i < selection.n; i++) { for(int i = 0; i < selection.n; i++) {
Selection *s = &selection.elem[i]; Selection *s = &selection[i];
if(s->entity.v != 0) { if(s->entity.v != 0) {
Entity *e = SK.entity.FindById(s->entity); Entity *e = SK.entity.FindById(s->entity);
if(e->IsFace()) { if(e->IsFace()) {
@ -844,10 +848,11 @@ void GraphicsWindow::EnsureValidActives() {
bool change = false; bool change = false;
// The active group must exist, and not be the references. // The active group must exist, and not be the references.
Group *g = SK.group.FindByIdNoOops(activeGroup); Group *g = SK.group.FindByIdNoOops(activeGroup);
if((!g) || (g->h.v == Group::HGROUP_REFERENCES.v)) { if((!g) || (g->h == Group::HGROUP_REFERENCES)) {
// Not using range-for because this is used to find an index.
int i; int i;
for(i = 0; i < SK.groupOrder.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
if(SK.groupOrder.elem[i].v != Group::HGROUP_REFERENCES.v) { if(SK.groupOrder[i] != Group::HGROUP_REFERENCES) {
break; break;
} }
} }
@ -863,7 +868,7 @@ void GraphicsWindow::EnsureValidActives() {
// do it now so that drawing mode isn't switched to "Free in 3d". // do it now so that drawing mode isn't switched to "Free in 3d".
SS.GenerateAll(SolveSpaceUI::Generate::ALL); SS.GenerateAll(SolveSpaceUI::Generate::ALL);
} else { } else {
activeGroup = SK.groupOrder.elem[i]; activeGroup = SK.groupOrder[i];
} }
SK.GetGroup(activeGroup)->Activate(); SK.GetGroup(activeGroup)->Activate();
change = true; change = true;
@ -874,7 +879,7 @@ void GraphicsWindow::EnsureValidActives() {
Entity *e = SK.entity.FindByIdNoOops(ActiveWorkplane()); Entity *e = SK.entity.FindByIdNoOops(ActiveWorkplane());
if(e) { if(e) {
hGroup hgw = e->group; hGroup hgw = e->group;
if(hgw.v != activeGroup.v && SS.GroupsInOrder(activeGroup, hgw)) { if(hgw != activeGroup && SS.GroupsInOrder(activeGroup, hgw)) {
// The active workplane is in a group that comes after the // The active workplane is in a group that comes after the
// active group; so any request or constraint will fail. // active group; so any request or constraint will fail.
SetWorkplaneFreeIn3d(); SetWorkplaneFreeIn3d();
@ -931,7 +936,7 @@ hEntity GraphicsWindow::ActiveWorkplane() {
} }
} }
bool GraphicsWindow::LockedInWorkplane() { bool GraphicsWindow::LockedInWorkplane() {
return (SS.GW.ActiveWorkplane().v != Entity::FREE_IN_3D.v); return (SS.GW.ActiveWorkplane() != Entity::FREE_IN_3D);
} }
void GraphicsWindow::ForceTextWindowShown() { void GraphicsWindow::ForceTextWindowShown() {
@ -1026,7 +1031,7 @@ void GraphicsWindow::MenuEdit(Command id) {
case Command::SELECT_ALL: { case Command::SELECT_ALL: {
Entity *e; Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->group.v != SS.GW.activeGroup.v) continue; if(e->group != SS.GW.activeGroup) continue;
if(e->IsFace() || e->IsDistance()) continue; if(e->IsFace() || e->IsDistance()) continue;
if(!e->IsVisible()) continue; if(!e->IsVisible()) continue;
@ -1044,7 +1049,7 @@ void GraphicsWindow::MenuEdit(Command id) {
do { do {
didSomething = false; didSomething = false;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->group.v != SS.GW.activeGroup.v) continue; if(e->group != SS.GW.activeGroup) continue;
if(!e->HasEndpoints()) continue; if(!e->HasEndpoints()) continue;
if(!e->IsVisible()) continue; if(!e->IsVisible()) continue;
@ -1055,7 +1060,7 @@ void GraphicsWindow::MenuEdit(Command id) {
List<Selection> *ls = &(SS.GW.selection); List<Selection> *ls = &(SS.GW.selection);
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) { for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
if(!s->entity.v) continue; if(!s->entity.v) continue;
if(s->entity.v == e->h.v) { if(s->entity == e->h) {
alreadySelected = true; alreadySelected = true;
continue; continue;
} }
@ -1302,7 +1307,7 @@ void GraphicsWindow::ToggleBool(bool *v) {
// If the mesh or edges were previously hidden, they haven't been generated, // If the mesh or edges were previously hidden, they haven't been generated,
// and if we are going to show them, we need to generate them first. // and if we are going to show them, we need to generate them first.
Group *g = SK.GetGroup(SS.GW.activeGroup); Group *g = SK.GetGroup(SS.GW.activeGroup);
if(*v && (g->displayOutlines.l.n == 0 && (v == &showEdges || v == &showOutlines))) { if(*v && (g->displayOutlines.l.IsEmpty() && (v == &showEdges || v == &showOutlines))) {
SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE); SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE);
} }

View File

@ -49,8 +49,8 @@ bool Group::IsVisible() {
return true; return true;
} }
size_t Group::GetNumConstraints(void) { size_t Group::GetNumConstraints() {
return SK.constraint.CountIf([&](Constraint const & c) { return c.group.v == h.v; }); return SK.constraint.CountIf([&](Constraint const & c) { return c.group == h; });
} }
Vector Group::ExtrusionGetVector() { Vector Group::ExtrusionGetVector() {
@ -163,6 +163,10 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
break; break;
case Command::GROUP_LATHE: case Command::GROUP_LATHE:
if(!SS.GW.LockedInWorkplane()) {
Error(_("Lathe operation can only be applied to planar sketches."));
return;
}
if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) { if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
g.predef.origin = gs.point[0]; g.predef.origin = gs.point[0];
g.predef.entityB = gs.vector[0]; g.predef.entityB = gs.vector[0];
@ -184,6 +188,62 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
g.name = C_("group-name", "lathe"); g.name = C_("group-name", "lathe");
break; break;
case Command::GROUP_REVOLVE:
if(!SS.GW.LockedInWorkplane()) {
Error(_("Revolve operation can only be applied to planar sketches."));
return;
}
if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
g.predef.origin = gs.point[0];
g.predef.entityB = gs.vector[0];
} else if(gs.lineSegments == 1 && gs.n == 1) {
g.predef.origin = SK.GetEntity(gs.entity[0])->point[0];
g.predef.entityB = gs.entity[0];
// since a line segment is a vector
} else {
Error(_("Bad selection for new revolve group. This group can "
"be created with:\n\n"
" * a point and a line segment or normal "
"(revolved about an axis parallel to line / "
"normal, through point)\n"
" * a line segment (revolved about line segment)\n"));
return;
}
g.type = Type::REVOLVE;
g.opA = SS.GW.activeGroup;
g.valA = 2;
g.subtype = Subtype::ONE_SIDED;
g.name = C_("group-name", "revolve");
break;
case Command::GROUP_HELIX:
if(!SS.GW.LockedInWorkplane()) {
Error(_("Helix operation can only be applied to planar sketches."));
return;
}
if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
g.predef.origin = gs.point[0];
g.predef.entityB = gs.vector[0];
} else if(gs.lineSegments == 1 && gs.n == 1) {
g.predef.origin = SK.GetEntity(gs.entity[0])->point[0];
g.predef.entityB = gs.entity[0];
// since a line segment is a vector
} else {
Error(_("Bad selection for new helix group. This group can "
"be created with:\n\n"
" * a point and a line segment or normal "
"(revolved about an axis parallel to line / "
"normal, through point)\n"
" * a line segment (revolved about line segment)\n"));
return;
}
g.type = Type::HELIX;
g.opA = SS.GW.activeGroup;
g.valA = 2;
g.subtype = Subtype::ONE_SIDED;
g.name = C_("group-name", "helix");
break;
case Command::GROUP_ROT: { case Command::GROUP_ROT: {
if(gs.points == 1 && gs.n == 1 && SS.GW.LockedInWorkplane()) { if(gs.points == 1 && gs.n == 1 && SS.GW.LockedInWorkplane()) {
g.predef.origin = gs.point[0]; g.predef.origin = gs.point[0];
@ -249,7 +309,7 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
} }
// Copy color from the previous mesh-contributing group. // Copy color from the previous mesh-contributing group.
if(g.IsMeshGroup() && SK.groupOrder.n > 0) { if(g.IsMeshGroup() && !SK.groupOrder.IsEmpty()) {
Group *running = SK.GetRunningMeshGroupFor(SS.GW.activeGroup); Group *running = SK.GetRunningMeshGroupFor(SS.GW.activeGroup);
if(running != NULL) { if(running != NULL) {
g.color = running->color; g.color = running->color;
@ -260,11 +320,11 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
SS.UndoRemember(); SS.UndoRemember();
bool afterActive = false; bool afterActive = false;
for(int i = 0; i < SK.groupOrder.n; i++) { for(hGroup hg : SK.groupOrder) {
Group *gi = SK.GetGroup(SK.groupOrder.elem[i]); Group *gi = SK.GetGroup(hg);
if(afterActive) if(afterActive)
gi->order += 1; gi->order += 1;
if(gi->h.v == SS.GW.activeGroup.v) { if(gi->h == SS.GW.activeGroup) {
g.order = gi->order + 1; g.order = gi->order + 1;
afterActive = true; afterActive = true;
} }
@ -344,7 +404,7 @@ std::string Group::DescriptionString() {
void Group::Activate() { void Group::Activate() {
if(type == Type::EXTRUDE || type == Type::LINKED || type == Type::LATHE || if(type == Type::EXTRUDE || type == Type::LINKED || type == Type::LATHE ||
type == Type::TRANSLATE || type == Type::ROTATE) { type == Type::REVOLVE || type == Type::HELIX || type == Type::TRANSLATE || type == Type::ROTATE) {
SS.GW.showFaces = true; SS.GW.showFaces = true;
} else { } else {
SS.GW.showFaces = false; SS.GW.showFaces = false;
@ -424,9 +484,10 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
// Get some arbitrary point in the sketch, that will be used // Get some arbitrary point in the sketch, that will be used
// as a reference when defining top and bottom faces. // as a reference when defining top and bottom faces.
hEntity pt = { 0 }; hEntity pt = { 0 };
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) { for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->elem[i]); Entity *e = &(entity->Get(i));
if(e->group.v != opA.v) continue; if(e->group != opA) continue;
if(e->IsPoint()) pt = e->h; if(e->IsPoint()) pt = e->h;
@ -436,11 +497,11 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
// adds entities, which may cause a realloc. // adds entities, which may cause a realloc.
CopyEntity(entity, SK.GetEntity(he), ai, REMAP_BOTTOM, CopyEntity(entity, SK.GetEntity(he), ai, REMAP_BOTTOM,
h.param(0), h.param(1), h.param(2), h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::N_TRANS); CopyAs::N_TRANS);
CopyEntity(entity, SK.GetEntity(he), af, REMAP_TOP, CopyEntity(entity, SK.GetEntity(he), af, REMAP_TOP,
h.param(0), h.param(1), h.param(2), h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::N_TRANS); CopyAs::N_TRANS);
MakeExtrusionLines(entity, he); MakeExtrusionLines(entity, he);
} }
@ -457,9 +518,10 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
// Remapped entity index. // Remapped entity index.
int ai = 1; int ai = 1;
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) { for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->elem[i]); Entity *e = &(entity->Get(i));
if(e->group.v != opA.v) continue; if(e->group != opA) continue;
e->CalculateNumerical(/*forExport=*/false); e->CalculateNumerical(/*forExport=*/false);
hEntity he = e->h; hEntity he = e->h;
@ -468,20 +530,133 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
// adds entities, which may cause a realloc. // adds entities, which may cause a realloc.
CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai, CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::NUMERIC); CopyAs::NUMERIC);
CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_START, CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_START,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::NUMERIC); CopyAs::NUMERIC);
CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_END, CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_END,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::NUMERIC); CopyAs::NUMERIC);
MakeLatheCircles(entity, param, he, axis_pos, axis_dir, ai); MakeLatheCircles(entity, param, he, axis_pos, axis_dir, ai);
MakeLatheSurfacesSelectable(entity, he, axis_dir);
ai++;
}
return;
}
case Type::REVOLVE: {
// this was borrowed from LATHE and ROTATE
Vector axis_pos = SK.GetEntity(predef.origin)->PointGetNum();
Vector axis_dir = SK.GetEntity(predef.entityB)->VectorGetNum();
// The center of rotation
AddParam(param, h.param(0), axis_pos.x);
AddParam(param, h.param(1), axis_pos.y);
AddParam(param, h.param(2), axis_pos.z);
// The rotation quaternion
AddParam(param, h.param(3), 30 * PI / 180);
AddParam(param, h.param(4), axis_dir.x);
AddParam(param, h.param(5), axis_dir.y);
AddParam(param, h.param(6), axis_dir.z);
int ai = 1;
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->Get(i));
if(e->group != opA)
continue;
e->CalculateNumerical(/*forExport=*/false);
hEntity he = e->h;
CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC);
for(a = 0; a < 2; a++) {
//! @todo is this check redundant?
Entity *e = &(entity->Get(i));
if(e->group != opA)
continue;
e->CalculateNumerical(false);
CopyEntity(entity, e, a * 2 - (subtype == Subtype::ONE_SIDED ? 0 : 1),
(a == 1) ? REMAP_LATHE_END : REMAP_LATHE_START, h.param(0),
h.param(1), h.param(2), h.param(3), h.param(4), h.param(5),
h.param(6), NO_PARAM, CopyAs::N_ROT_AA);
}
// Arcs are not generated for revolve groups, for now, because our current arc
// entity is not chiral, and dragging a revolve may break the arc by inverting it.
// MakeLatheCircles(entity, param, he, axis_pos, axis_dir, ai);
MakeLatheSurfacesSelectable(entity, he, axis_dir);
ai++;
}
return;
}
case Type::HELIX: {
Vector axis_pos = SK.GetEntity(predef.origin)->PointGetNum();
Vector axis_dir = SK.GetEntity(predef.entityB)->VectorGetNum();
// The center of rotation
AddParam(param, h.param(0), axis_pos.x);
AddParam(param, h.param(1), axis_pos.y);
AddParam(param, h.param(2), axis_pos.z);
// The rotation quaternion
AddParam(param, h.param(3), 30 * PI / 180);
AddParam(param, h.param(4), axis_dir.x);
AddParam(param, h.param(5), axis_dir.y);
AddParam(param, h.param(6), axis_dir.z);
// distance to translate along the rotation axis
AddParam(param, h.param(7), 20);
int ai = 1;
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->Get(i));
if((e->group.v != opA.v) && !(e->h == predef.origin))
continue;
e->CalculateNumerical(/*forExport=*/false);
CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC);
for(a = 0; a < 2; a++) {
Entity *e = &(entity->Get(i));
e->CalculateNumerical(false);
CopyEntity(entity, e, a * 2 - (subtype == Subtype::ONE_SIDED ? 0 : 1),
(a == 1) ? REMAP_LATHE_END : REMAP_LATHE_START, h.param(0),
h.param(1), h.param(2), h.param(3), h.param(4), h.param(5),
h.param(6), h.param(7), CopyAs::N_ROT_AXIS_TRANS);
}
// For point entities on the axis, create a construction line
e = &(entity->Get(i));
if(e->IsPoint()) {
Vector check = e->PointGetNum().Minus(axis_pos).Cross(axis_dir);
if (check.Dot(check) < LENGTH_EPS) {
//! @todo isn't this the same as &(ent[i])?
Entity *ep = SK.GetEntity(e->h);
Entity en = {};
// A point gets extruded to form a line segment
en.point[0] = Remap(ep->h, REMAP_LATHE_START);
en.point[1] = Remap(ep->h, REMAP_LATHE_END);
en.group = h;
en.construction = ep->construction;
en.style = ep->style;
en.h = Remap(ep->h, REMAP_PT_TO_LINE);
en.type = Entity::Type::LINE_SEGMENT;
entity->Add(&en);
}
}
ai++; ai++;
} }
return; return;
@ -502,16 +677,17 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
} }
for(a = a0; a < n; a++) { for(a = a0; a < n; a++) {
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) { for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->elem[i]); Entity *e = &(entity->Get(i));
if(e->group.v != opA.v) continue; if(e->group != opA) continue;
e->CalculateNumerical(/*forExport=*/false); e->CalculateNumerical(/*forExport=*/false);
CopyEntity(entity, e, CopyEntity(entity, e,
a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)), a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)),
(a == (n - 1)) ? REMAP_LAST : a, (a == (n - 1)) ? REMAP_LAST : a,
h.param(0), h.param(1), h.param(2), h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::N_TRANS); CopyAs::N_TRANS);
} }
} }
@ -537,16 +713,17 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
} }
for(a = a0; a < n; a++) { for(a = a0; a < n; a++) {
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) { for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->elem[i]); Entity *e = &(entity->Get(i));
if(e->group.v != opA.v) continue; if(e->group != opA) continue;
e->CalculateNumerical(/*forExport=*/false); e->CalculateNumerical(/*forExport=*/false);
CopyEntity(entity, e, CopyEntity(entity, e,
a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)), a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)),
(a == (n - 1)) ? REMAP_LAST : a, (a == (n - 1)) ? REMAP_LAST : a,
h.param(0), h.param(1), h.param(2), h.param(0), h.param(1), h.param(2),
h.param(3), h.param(4), h.param(5), h.param(6), h.param(3), h.param(4), h.param(5), h.param(6), NO_PARAM,
CopyAs::N_ROT_AA); CopyAs::N_ROT_AA);
} }
} }
@ -563,11 +740,12 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
AddParam(param, h.param(5), 0); AddParam(param, h.param(5), 0);
AddParam(param, h.param(6), 0); AddParam(param, h.param(6), 0);
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < impEntity.n; i++) { for(i = 0; i < impEntity.n; i++) {
Entity *ie = &(impEntity.elem[i]); Entity *ie = &(impEntity[i]);
CopyEntity(entity, ie, 0, 0, CopyEntity(entity, ie, 0, 0,
h.param(0), h.param(1), h.param(2), h.param(0), h.param(1), h.param(2),
h.param(3), h.param(4), h.param(5), h.param(6), h.param(3), h.param(4), h.param(5), h.param(6), NO_PARAM,
CopyAs::N_ROT_TRANS); CopyAs::N_ROT_TRANS);
} }
return; return;
@ -596,7 +774,7 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
Expr::From(h.param(5)), Expr::From(h.param(5)),
Expr::From(h.param(6)) }; Expr::From(h.param(6)) };
AddEq(l, (q.Magnitude())->Minus(Expr::From(1)), 0); AddEq(l, (q.Magnitude())->Minus(Expr::From(1)), 0);
} else if(type == Type::ROTATE) { } else if(type == Type::ROTATE || type == Type::REVOLVE || type == Type::HELIX) {
// The axis and center of rotation are specified numerically // The axis and center of rotation are specified numerically
#define EC(x) (Expr::From(x)) #define EC(x) (Expr::From(x))
#define EP(x) (Expr::From(h.param(x))) #define EP(x) (Expr::From(h.param(x)))
@ -613,7 +791,7 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
#undef EC #undef EC
#undef EP #undef EP
} else if(type == Type::EXTRUDE) { } else if(type == Type::EXTRUDE) {
if(predef.entityB.v != Entity::FREE_IN_3D.v) { if(predef.entityB != Entity::FREE_IN_3D) {
// The extrusion path is locked along a line, normal to the // The extrusion path is locked along a line, normal to the
// specified workplane. // specified workplane.
Entity *w = SK.GetEntity(predef.entityB); Entity *w = SK.GetEntity(predef.entityB);
@ -628,7 +806,7 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
AddEq(l, v.Dot(extruden), 1); AddEq(l, v.Dot(extruden), 1);
} }
} else if(type == Type::TRANSLATE) { } else if(type == Type::TRANSLATE) {
if(predef.entityB.v != Entity::FREE_IN_3D.v) { if(predef.entityB != Entity::FREE_IN_3D) {
Entity *w = SK.GetEntity(predef.entityB); Entity *w = SK.GetEntity(predef.entityB);
ExprVector n = w->Normal()->NormalExprsN(); ExprVector n = w->Normal()->NormalExprsN();
ExprVector trans; ExprVector trans;
@ -644,7 +822,7 @@ hEntity Group::Remap(hEntity in, int copyNumber) {
auto it = remap.find({ in, copyNumber }); auto it = remap.find({ in, copyNumber });
if(it == remap.end()) { if(it == remap.end()) {
std::tie(it, std::ignore) = std::tie(it, std::ignore) =
remap.insert({ { in, copyNumber }, { (uint32_t)remap.size() } }); remap.insert({ { in, copyNumber }, { (uint32_t)remap.size() + 1 } });
} }
return h.entity(it->second.v); return h.entity(it->second.v);
} }
@ -729,7 +907,14 @@ void Group::MakeLatheCircles(IdList<Entity,hEntity> *el, IdList<Param,hParam> *p
el->Add(&n); el->Add(&n);
en.normal = n.h; en.normal = n.h;
el->Add(&en); el->Add(&en);
} else if(ep->type == Entity::Type::LINE_SEGMENT) { }
}
void Group::MakeLatheSurfacesSelectable(IdList<Entity, hEntity> *el, hEntity in, Vector axis) {
Entity *ep = SK.GetEntity(in);
Entity en = {};
if(ep->type == Entity::Type::LINE_SEGMENT) {
// An axis-perpendicular line gets revolved to form a face. // An axis-perpendicular line gets revolved to form a face.
Vector a = SK.GetEntity(ep->point[0])->PointGetNum(); Vector a = SK.GetEntity(ep->point[0])->PointGetNum();
Vector b = SK.GetEntity(ep->point[1])->PointGetNum(); Vector b = SK.GetEntity(ep->point[1])->PointGetNum();
@ -786,7 +971,7 @@ void Group::MakeExtrusionTopBottomFaces(IdList<Entity,hEntity> *el, hEntity pt)
void Group::CopyEntity(IdList<Entity,hEntity> *el, void Group::CopyEntity(IdList<Entity,hEntity> *el,
Entity *ep, int timesApplied, int remap, Entity *ep, int timesApplied, int remap,
hParam dx, hParam dy, hParam dz, hParam dx, hParam dy, hParam dz,
hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam dist,
CopyAs as) CopyAs as)
{ {
Entity en = {}; Entity en = {};
@ -810,6 +995,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
case Entity::Type::POINT_N_TRANS: case Entity::Type::POINT_N_TRANS:
case Entity::Type::POINT_N_ROT_TRANS: case Entity::Type::POINT_N_ROT_TRANS:
case Entity::Type::POINT_N_ROT_AA: case Entity::Type::POINT_N_ROT_AA:
case Entity::Type::POINT_N_ROT_AXIS_TRANS:
case Entity::Type::POINT_IN_3D: case Entity::Type::POINT_IN_3D:
case Entity::Type::POINT_IN_2D: case Entity::Type::POINT_IN_2D:
if(as == CopyAs::N_TRANS) { if(as == CopyAs::N_TRANS) {
@ -822,6 +1008,8 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
} else { } else {
if(as == CopyAs::N_ROT_AA) { if(as == CopyAs::N_ROT_AA) {
en.type = Entity::Type::POINT_N_ROT_AA; en.type = Entity::Type::POINT_N_ROT_AA;
} else if (as == CopyAs::N_ROT_AXIS_TRANS) {
en.type = Entity::Type::POINT_N_ROT_AXIS_TRANS;
} else { } else {
en.type = Entity::Type::POINT_N_ROT_TRANS; en.type = Entity::Type::POINT_N_ROT_TRANS;
} }
@ -832,6 +1020,9 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
en.param[4] = qvx; en.param[4] = qvx;
en.param[5] = qvy; en.param[5] = qvy;
en.param[6] = qvz; en.param[6] = qvz;
if (as == CopyAs::N_ROT_AXIS_TRANS) {
en.param[7] = dist;
}
} }
en.numPoint = (ep->actPoint).ScaledBy(scale); en.numPoint = (ep->actPoint).ScaledBy(scale);
break; break;
@ -843,8 +1034,8 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
case Entity::Type::NORMAL_IN_2D: case Entity::Type::NORMAL_IN_2D:
if(as == CopyAs::N_TRANS || as == CopyAs::NUMERIC) { if(as == CopyAs::N_TRANS || as == CopyAs::NUMERIC) {
en.type = Entity::Type::NORMAL_N_COPY; en.type = Entity::Type::NORMAL_N_COPY;
} else { } else { // N_ROT_AXIS_TRANS probably doesn't warrant a new entity Type
if(as == CopyAs::N_ROT_AA) { if(as == CopyAs::N_ROT_AA || as == CopyAs::N_ROT_AXIS_TRANS) {
en.type = Entity::Type::NORMAL_N_ROT_AA; en.type = Entity::Type::NORMAL_N_ROT_AA;
} else { } else {
en.type = Entity::Type::NORMAL_N_ROT; en.type = Entity::Type::NORMAL_N_ROT;
@ -879,7 +1070,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
} else if (as == CopyAs::NUMERIC) { } else if (as == CopyAs::NUMERIC) {
en.type = Entity::Type::FACE_NORMAL_PT; en.type = Entity::Type::FACE_NORMAL_PT;
} else { } else {
if(as == CopyAs::N_ROT_AA) { if(as == CopyAs::N_ROT_AA || as == CopyAs::N_ROT_AXIS_TRANS) {
en.type = Entity::Type::FACE_N_ROT_AA; en.type = Entity::Type::FACE_N_ROT_AA;
} else { } else {
en.type = Entity::Type::FACE_N_ROT_TRANS; en.type = Entity::Type::FACE_N_ROT_TRANS;

View File

@ -14,13 +14,15 @@ void Group::AssembleLoops(bool *allClosed,
SBezierList sbl = {}; SBezierList sbl = {};
int i; int i;
for(i = 0; i < SK.entity.n; i++) { for(auto &e : SK.entity) {
Entity *e = &(SK.entity.elem[i]); if(e.group != h)
if(e->group.v != h.v) continue; continue;
if(e->construction) continue; if(e.construction)
if(e->forceHidden) continue; continue;
if(e.forceHidden)
continue;
e->GenerateBezierCurves(&sbl); e.GenerateBezierCurves(&sbl);
} }
SBezier *sb; SBezier *sb;
@ -84,7 +86,7 @@ void SShell::RemapFaces(Group *g, int remap) {
SSurface *ss; SSurface *ss;
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)){ for(ss = surface.First(); ss; ss = surface.NextAfter(ss)){
hEntity face = { ss->face }; hEntity face = { ss->face };
if(face.v == Entity::NO_ENTITY.v) continue; if(face == Entity::NO_ENTITY) continue;
face = g->Remap(face, remap); face = g->Remap(face, remap);
ss->face = face.v; ss->face = face.v;
@ -95,7 +97,7 @@ void SMesh::RemapFaces(Group *g, int remap) {
STriangle *tr; STriangle *tr;
for(tr = l.First(); tr; tr = l.NextAfter(tr)) { for(tr = l.First(); tr; tr = l.NextAfter(tr)) {
hEntity face = { tr->meta.face }; hEntity face = { tr->meta.face };
if(face.v == Entity::NO_ENTITY.v) continue; if(face == Entity::NO_ENTITY) continue;
face = g->Remap(face, remap); face = g->Remap(face, remap);
tr->meta.face = face.v; tr->meta.face = face.v;
@ -191,7 +193,7 @@ void Group::GenerateShellAndMesh() {
// Don't attempt a lathe or extrusion unless the source section is good: // Don't attempt a lathe or extrusion unless the source section is good:
// planar and not self-intersecting. // planar and not self-intersecting.
bool haveSrc = true; bool haveSrc = true;
if(type == Type::EXTRUDE || type == Type::LATHE) { if(type == Type::EXTRUDE || type == Type::LATHE || type == Type::REVOLVE) {
Group *src = SK.GetGroup(opA); Group *src = SK.GetGroup(opA);
if(src->polyError.how != PolyError::GOOD) { if(src->polyError.how != PolyError::GOOD) {
haveSrc = false; haveSrc = false;
@ -235,8 +237,10 @@ void Group::GenerateShellAndMesh() {
// that face, so that the user can select them with the mouse. // that face, so that the user can select them with the mouse.
Vector onOrig = sbls->point; Vector onOrig = sbls->point;
int i; int i;
// Not using range-for here because we're starting at a different place and using
// indices for meaning.
for(i = is; i < thisShell.surface.n; i++) { for(i = is; i < thisShell.surface.n; i++) {
SSurface *ss = &(thisShell.surface.elem[i]); SSurface *ss = &(thisShell.surface[i]);
hEntity face = Entity::NO_ENTITY; hEntity face = Entity::NO_ENTITY;
Vector p = ss->PointAt(0, 0), Vector p = ss->PointAt(0, 0),
@ -261,7 +265,7 @@ void Group::GenerateShellAndMesh() {
Entity *e; Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->group.v != opA.v) continue; if(e->group != opA) continue;
if(e->type != Entity::Type::LINE_SEGMENT) continue; if(e->type != Entity::Type::LINE_SEGMENT) continue;
Vector a = SK.GetEntity(e->point[0])->PointGetNum(), Vector a = SK.GetEntity(e->point[0])->PointGetNum(),
@ -293,6 +297,51 @@ void Group::GenerateShellAndMesh() {
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) { for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
thisShell.MakeFromRevolutionOf(sbls, pt, axis, color, this); thisShell.MakeFromRevolutionOf(sbls, pt, axis, color, this);
} }
} else if(type == Type::REVOLVE && haveSrc) {
Group *src = SK.GetGroup(opA);
double anglef = SK.GetParam(h.param(3))->val * 4; // why the 4 is needed?
double dists = 0, distf = 0;
double angles = 0.0;
if(subtype != Subtype::ONE_SIDED) {
anglef *= 0.5;
angles = -anglef;
}
Vector pt = SK.GetEntity(predef.origin)->PointGetNum(),
axis = SK.GetEntity(predef.entityB)->VectorGetNum();
axis = axis.WithMagnitude(1);
SBezierLoopSetSet *sblss = &(src->bezierLoops);
SBezierLoopSet *sbls;
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
if(fabs(anglef - angles) < 2 * PI) {
thisShell.MakeFromHelicalRevolutionOf(sbls, pt, axis, color, this,
angles, anglef, dists, distf);
} else {
thisShell.MakeFromRevolutionOf(sbls, pt, axis, color, this);
}
}
} else if(type == Type::HELIX && haveSrc) {
Group *src = SK.GetGroup(opA);
double anglef = SK.GetParam(h.param(3))->val * 4; // why the 4 is needed?
double dists = 0, distf = 0;
double angles = 0.0;
distf = SK.GetParam(h.param(7))->val * 2; // dist is applied twice
if(subtype != Subtype::ONE_SIDED) {
anglef *= 0.5;
angles = -anglef;
distf *= 0.5;
dists = -distf;
}
Vector pt = SK.GetEntity(predef.origin)->PointGetNum(),
axis = SK.GetEntity(predef.entityB)->VectorGetNum();
axis = axis.WithMagnitude(1);
SBezierLoopSetSet *sblss = &(src->bezierLoops);
SBezierLoopSet *sbls;
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
thisShell.MakeFromHelicalRevolutionOf(sbls, pt, axis, color, this,
angles, anglef, dists, distf);
}
} else if(type == Type::LINKED) { } else if(type == Type::LINKED) {
// The imported shell or mesh are copied over, with the appropriate // The imported shell or mesh are copied over, with the appropriate
// transformation applied. We also must remap the face entities. // transformation applied. We also must remap the face entities.
@ -417,7 +466,7 @@ void Group::GenerateDisplayItems() {
if(SS.GW.showEdges || SS.GW.showOutlines) { if(SS.GW.showEdges || SS.GW.showOutlines) {
SOutlineList rawOutlines = {}; SOutlineList rawOutlines = {};
if(runningMesh.l.n > 0) { if(!runningMesh.l.IsEmpty()) {
// Triangle mesh only; no shell or emphasized edges. // Triangle mesh only; no shell or emphasized edges.
runningMesh.MakeOutlinesInto(&rawOutlines, EdgeKind::EMPHASIZED); runningMesh.MakeOutlinesInto(&rawOutlines, EdgeKind::EMPHASIZED);
} else { } else {
@ -437,7 +486,7 @@ void Group::GenerateDisplayItems() {
displayMesh.PrecomputeTransparency(); displayMesh.PrecomputeTransparency();
// Recalculate mass center if needed // Recalculate mass center if needed
if(SS.centerOfMass.draw && SS.centerOfMass.dirty && h.v == SS.GW.activeGroup.v) { if(SS.centerOfMass.draw && SS.centerOfMass.dirty && h == SS.GW.activeGroup) {
SS.UpdateCenterOfMass(); SS.UpdateCenterOfMass();
} }
displayDirty = false; displayDirty = false;
@ -445,13 +494,15 @@ void Group::GenerateDisplayItems() {
} }
Group *Group::PreviousGroup() const { Group *Group::PreviousGroup() const {
int i; Group *prev = nullptr;
for(i = 0; i < SK.groupOrder.n; i++) { for(auto const &gh : SK.groupOrder) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]); Group *g = SK.GetGroup(gh);
if(g->h.v == h.v) break; if(g->h == h) {
return prev;
}
prev = g;
} }
if(i == 0 || i >= SK.groupOrder.n) return NULL; return nullptr;
return SK.GetGroup(SK.groupOrder.elem[i - 1]);
} }
Group *Group::RunningMeshGroup() const { Group *Group::RunningMeshGroup() const {
@ -466,6 +517,8 @@ bool Group::IsMeshGroup() {
switch(type) { switch(type) {
case Group::Type::EXTRUDE: case Group::Type::EXTRUDE:
case Group::Type::LATHE: case Group::Type::LATHE:
case Group::Type::REVOLVE:
case Group::Type::HELIX:
case Group::Type::ROTATE: case Group::Type::ROTATE:
case Group::Type::TRANSLATE: case Group::Type::TRANSLATE:
return true; return true;
@ -653,11 +706,12 @@ void Group::DrawPolyError(Canvas *canvas) {
void Group::DrawFilledPaths(Canvas *canvas) { void Group::DrawFilledPaths(Canvas *canvas) {
for(const SBezierLoopSet &sbls : bezierLoops.l) { for(const SBezierLoopSet &sbls : bezierLoops.l) {
if(sbls.l.n == 0 || sbls.l.elem[0].l.n == 0) continue; if(sbls.l.IsEmpty() || sbls.l[0].l.IsEmpty())
continue;
// In an assembled loop, all the styles should be the same; so doesn't // In an assembled loop, all the styles should be the same; so doesn't
// matter which one we grab. // matter which one we grab.
SBezier *sb = &(sbls.l.elem[0].l.elem[0]); const SBezier *sb = &(sbls.l[0].l[0]);
Style *s = Style::Get({ (uint32_t)sb->auxA }); Style *s = Style::Get({ (uint32_t)sb->auxA });
Canvas::Fill fill = {}; Canvas::Fill fill = {};
@ -665,7 +719,7 @@ void Group::DrawFilledPaths(Canvas *canvas) {
if(s->filled) { if(s->filled) {
// This is a filled loop, where the user specified a fill color. // This is a filled loop, where the user specified a fill color.
fill.color = s->fillColor; fill.color = s->fillColor;
} else if(h.v == SS.GW.activeGroup.v && SS.checkClosedContour && } else if(h == SS.GW.activeGroup && SS.checkClosedContour &&
polyError.how == PolyError::GOOD) { polyError.how == PolyError::GOOD) {
// If this is the active group, and we are supposed to check // If this is the active group, and we are supposed to check
// for closed contours, and we do indeed have a closed and // for closed contours, and we do indeed have a closed and
@ -687,9 +741,10 @@ void Group::DrawContourAreaLabels(Canvas *canvas) {
Vector gu = camera.projUp.ScaledBy(1 / camera.scale); Vector gu = camera.projUp.ScaledBy(1 / camera.scale);
for(SBezierLoopSet &sbls : bezierLoops.l) { for(SBezierLoopSet &sbls : bezierLoops.l) {
if(sbls.l.n == 0 || sbls.l.elem[0].l.n == 0) continue; if(sbls.l.IsEmpty() || sbls.l[0].l.IsEmpty())
continue;
Vector min = sbls.l.elem[0].l.elem[0].ctrl[0]; Vector min = sbls.l[0].l[0].ctrl[0];
Vector max = min; Vector max = min;
Vector zero = Vector::From(0.0, 0.0, 0.0); Vector zero = Vector::From(0.0, 0.0, 0.0);
sbls.GetBoundingProjd(Vector::From(1.0, 0.0, 0.0), zero, &min.x, &max.x); sbls.GetBoundingProjd(Vector::From(1.0, 0.0, 0.0), zero, &min.x, &max.x);

View File

@ -340,7 +340,7 @@ public:
hStyle styleFor(const DRW_Entity *e) { hStyle styleFor(const DRW_Entity *e) {
// Color. // Color.
// TODO: which color to choose: index or RGB one? //! @todo which color to choose: index or RGB one?
int col = getColor(e); int col = getColor(e);
RgbaColor c = RgbaColor::From(DRW::dxfColors[col][0], RgbaColor c = RgbaColor::From(DRW::dxfColors[col][0],
DRW::dxfColors[col][1], DRW::dxfColors[col][1],
@ -352,7 +352,7 @@ public:
if(width < 0.0) width = 1.0; if(width < 0.0) width = 1.0;
// Line stipple. // Line stipple.
// TODO: Probably, we can load default autocad patterns and match it with ours. //! @todo Probably, we can load default autocad patterns and match it with ours.
std::string lineType = getLineType(e); std::string lineType = getLineType(e);
StipplePattern stipple = StipplePattern::CONTINUOUS; StipplePattern stipple = StipplePattern::CONTINUOUS;
for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) { for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
@ -455,8 +455,8 @@ public:
Entity *e = SK.GetEntity(he); Entity *e = SK.GetEntity(he);
Vector pos = e->PointGetNum(); Vector pos = e->PointGetNum();
hEntity p = findPoint(pos); hEntity p = findPoint(pos);
if(p.v == he.v) return; if(p == he) return;
if(p.v != Entity::NO_ENTITY.v) { if(p != Entity::NO_ENTITY) {
if(constrain) { if(constrain) {
Constraint::ConstrainCoincident(he, p); Constraint::ConstrainCoincident(he, p);
} }
@ -475,7 +475,7 @@ public:
hEntity createOrGetPoint(const Vector &p) { hEntity createOrGetPoint(const Vector &p) {
hEntity he = findPoint(p); hEntity he = findPoint(p);
if(he.v != Entity::NO_ENTITY.v) return he; if(he != Entity::NO_ENTITY) return he;
hRequest hr = SS.GW.AddRequest(Request::Type::DATUM_POINT, /*rememberForUndo=*/false); hRequest hr = SS.GW.AddRequest(Request::Type::DATUM_POINT, /*rememberForUndo=*/false);
he = hr.entity(0); he = hr.entity(0);

View File

@ -182,7 +182,7 @@ default: dbp("bad constraint type %d", sc->type); return;
c.other2 = (sc->other2) ? true : false; c.other2 = (sc->other2) ? true : false;
c.Generate(&params); c.Generate(&params);
if(params.n > 0) { if(!params.IsEmpty()) {
for(Param &p : params) { for(Param &p : params) {
p.h = SK.param.AddAndAssignId(&p); p.h = SK.param.AddAndAssignId(&p);
c.valP = p.h; c.valP = p.h;
@ -240,7 +240,7 @@ default: dbp("bad constraint type %d", sc->type); return;
if(ssys->failed) { if(ssys->failed) {
// Copy over any the list of problematic constraints. // Copy over any the list of problematic constraints.
for(i = 0; i < ssys->faileds && i < bad.n; i++) { for(i = 0; i < ssys->faileds && i < bad.n; i++) {
ssys->failed[i] = bad.elem[i].v; ssys->failed[i] = bad[i].v;
} }
ssys->faileds = bad.n; ssys->faileds = bad.n;
} }

View File

@ -51,7 +51,7 @@ void SMesh::GetBounding(Vector *vmax, Vector *vmin) const {
*vmin = Vector::From( 1e12, 1e12, 1e12); *vmin = Vector::From( 1e12, 1e12, 1e12);
*vmax = Vector::From(-1e12, -1e12, -1e12); *vmax = Vector::From(-1e12, -1e12, -1e12);
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
STriangle *st = &(l.elem[i]); const STriangle *st = &(l[i]);
DoBounding(st->a, vmax, vmin); DoBounding(st->a, vmax, vmin);
DoBounding(st->b, vmax, vmin); DoBounding(st->b, vmax, vmin);
DoBounding(st->c, vmax, vmin); DoBounding(st->c, vmax, vmin);
@ -70,7 +70,7 @@ void SMesh::MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d) {
m.l.ClearTags(); m.l.ClearTags();
int i; int i;
for(i = 0; i < m.l.n; i++) { for(i = 0; i < m.l.n; i++) {
STriangle *tr = &(m.l.elem[i]); STriangle *tr = &(m.l[i]);
if((fabs(n.Dot(tr->a) - d) >= LENGTH_EPS) || if((fabs(n.Dot(tr->a) - d) >= LENGTH_EPS) ||
(fabs(n.Dot(tr->b) - d) >= LENGTH_EPS) || (fabs(n.Dot(tr->b) - d) >= LENGTH_EPS) ||
@ -96,7 +96,7 @@ void SMesh::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) {
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// When we are called, all of the triangles from l.elem[start] to the end must // When we are called, all of the triangles from l[start] to the end must
// be coplanar. So we try to find a set of fewer triangles that covers the // be coplanar. So we try to find a set of fewer triangles that covers the
// exact same area, in order to reduce the number of triangles in the mesh. // exact same area, in order to reduce the number of triangles in the mesh.
// We use this after a triangle has been split against the BSP. // We use this after a triangle has been split against the BSP.
@ -108,7 +108,7 @@ void SMesh::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) {
void SMesh::Simplify(int start) { void SMesh::Simplify(int start) {
int maxTriangles = (l.n - start) + 10; int maxTriangles = (l.n - start) + 10;
STriMeta meta = l.elem[start].meta; STriMeta meta = l[start].meta;
STriangle *tout = (STriangle *)MemAlloc(maxTriangles*sizeof(*tout)); STriangle *tout = (STriangle *)MemAlloc(maxTriangles*sizeof(*tout));
int toutc = 0; int toutc = 0;
@ -121,7 +121,7 @@ void SMesh::Simplify(int start) {
int i, j; int i, j;
for(i = start; i < l.n; i++) { for(i = start; i < l.n; i++) {
STriangle *tr = &(l.elem[i]); STriangle *tr = &(l[i]);
if(tr->MinAltitude() < LENGTH_EPS) { if(tr->MinAltitude() < LENGTH_EPS) {
tr->tag = 1; tr->tag = 1;
} else { } else {
@ -133,7 +133,7 @@ void SMesh::Simplify(int start) {
bool didAdd; bool didAdd;
convc = 0; convc = 0;
for(i = start; i < l.n; i++) { for(i = start; i < l.n; i++) {
STriangle *tr = &(l.elem[i]); STriangle *tr = &(l[i]);
if(tr->tag) continue; if(tr->tag) continue;
tr->tag = 1; tr->tag = 1;
@ -158,7 +158,7 @@ void SMesh::Simplify(int start) {
Vector c; Vector c;
for(i = start; i < l.n; i++) { for(i = start; i < l.n; i++) {
STriangle *tr = &(l.elem[i]); STriangle *tr = &(l[i]);
if(tr->tag) continue; if(tr->tag) continue;
if((tr->a).Equals(d) && (tr->b).Equals(b)) { if((tr->a).Equals(d) && (tr->b).Equals(b)) {
@ -242,7 +242,7 @@ void SMesh::AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3) {
int i; int i;
for(i = 0; i < srcm->l.n; i++) { for(i = 0; i < srcm->l.n; i++) {
STriangle *st = &(srcm->l.elem[i]); STriangle *st = &(srcm->l[i]);
int pn = l.n; int pn = l.n;
atLeastOneDiscarded = false; atLeastOneDiscarded = false;
SBsp3::InsertOrCreate(bsp3, st, this); SBsp3::InsertOrCreate(bsp3, st, this);
@ -289,7 +289,7 @@ void SMesh::MakeFromDifferenceOf(SMesh *a, SMesh *b) {
void SMesh::MakeFromCopyOf(SMesh *a) { void SMesh::MakeFromCopyOf(SMesh *a) {
ssassert(this != a, "Can't make from copy of self"); ssassert(this != a, "Can't make from copy of self");
for(int i = 0; i < a->l.n; i++) { for(int i = 0; i < a->l.n; i++) {
AddTriangle(&(a->l.elem[i])); AddTriangle(&(a->l[i]));
} }
} }
@ -318,9 +318,7 @@ void SMesh::MakeFromTransformationOf(SMesh *a, Vector trans,
} }
} }
bool SMesh::IsEmpty() const { bool SMesh::IsEmpty() const { return (l.IsEmpty()); }
return (l.n == 0);
}
uint32_t SMesh::FirstIntersectionWith(Point2d mp) const { uint32_t SMesh::FirstIntersectionWith(Point2d mp) const {
Vector rayPoint = SS.GW.UnProjectPoint3(Vector::From(mp.x, mp.y, 0.0)); Vector rayPoint = SS.GW.UnProjectPoint3(Vector::From(mp.x, mp.y, 0.0));
@ -329,7 +327,7 @@ uint32_t SMesh::FirstIntersectionWith(Point2d mp) const {
uint32_t face = 0; uint32_t face = 0;
double faceT = VERY_NEGATIVE; double faceT = VERY_NEGATIVE;
for(int i = 0; i < l.n; i++) { for(int i = 0; i < l.n; i++) {
const STriangle &tr = l.elem[i]; const STriangle &tr = l[i];
if(tr.meta.face == 0) continue; if(tr.meta.face == 0) continue;
double t; double t;
@ -347,7 +345,7 @@ Vector SMesh::GetCenterOfMass() const {
Vector center = {}; Vector center = {};
double vol = 0.0; double vol = 0.0;
for(int i = 0; i < l.n; i++) { for(int i = 0; i < l.n; i++) {
STriangle &tr = l.elem[i]; const STriangle &tr = l[i];
double tvol = tr.SignedVolume(); double tvol = tr.SignedVolume();
center = center.Plus(tr.a.Plus(tr.b.Plus(tr.c)).ScaledBy(tvol / 4.0)); center = center.Plus(tr.a.Plus(tr.b.Plus(tr.c)).ScaledBy(tvol / 4.0));
vol += tvol; vol += tvol;
@ -365,7 +363,7 @@ SKdNode *SKdNode::From(SMesh *m) {
STriangle *tra = (STriangle *)AllocTemporary((m->l.n) * sizeof(*tra)); STriangle *tra = (STriangle *)AllocTemporary((m->l.n) * sizeof(*tra));
for(i = 0; i < m->l.n; i++) { for(i = 0; i < m->l.n; i++) {
tra[i] = m->l.elem[i]; tra[i] = m->l[i];
} }
srand(0); srand(0);
@ -637,7 +635,7 @@ void SKdNode::SnapToVertex(Vector v, SMesh *extras) {
void SKdNode::SnapToMesh(SMesh *m) { void SKdNode::SnapToMesh(SMesh *m) {
int i, j, k; int i, j, k;
for(i = 0; i < m->l.n; i++) { for(i = 0; i < m->l.n; i++) {
STriangle *tr = &(m->l.elem[i]); STriangle *tr = &(m->l[i]);
if(tr->IsDegenerate()) { if(tr->IsDegenerate()) {
continue; continue;
} }
@ -649,7 +647,7 @@ void SKdNode::SnapToMesh(SMesh *m) {
for(k = 0; k < extra.l.n; k++) { for(k = 0; k < extra.l.n; k++) {
STriangle *tra = (STriangle *)AllocTemporary(sizeof(*tra)); STriangle *tra = (STriangle *)AllocTemporary(sizeof(*tra));
*tra = extra.l.elem[k]; *tra = extra.l[k];
AddTriangle(tra); AddTriangle(tra);
} }
extra.Clear(); extra.Clear();
@ -1130,3 +1128,67 @@ void SMesh::RemoveDegenerateTriangles() {
} }
l.RemoveTagged(); l.RemoveTagged();
} }
double SMesh::CalculateVolume() const {
double vol = 0;
for(STriangle tr : l) {
// Translate to place vertex A at (x, y, 0)
Vector trans = Vector::From(tr.a.x, tr.a.y, 0);
tr.a = (tr.a).Minus(trans);
tr.b = (tr.b).Minus(trans);
tr.c = (tr.c).Minus(trans);
// Rotate to place vertex B on the y-axis. Depending on
// whether the triangle is CW or CCW, C is either to the
// right or to the left of the y-axis. This handles the
// sign of our normal.
Vector u = Vector::From(-tr.b.y, tr.b.x, 0);
u = u.WithMagnitude(1);
Vector v = Vector::From(tr.b.x, tr.b.y, 0);
v = v.WithMagnitude(1);
Vector n = Vector::From(0, 0, 1);
tr.a = (tr.a).DotInToCsys(u, v, n);
tr.b = (tr.b).DotInToCsys(u, v, n);
tr.c = (tr.c).DotInToCsys(u, v, n);
n = tr.Normal().WithMagnitude(1);
// Triangles on edge don't contribute
if(fabs(n.z) < LENGTH_EPS) continue;
// The plane has equation p dot n = a dot n
double d = (tr.a).Dot(n);
// nx*x + ny*y + nz*z = d
// nz*z = d - nx*x - ny*y
double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z;
double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x;
double xc = tr.c.x, yb = tr.b.y;
// I asked Maple for
// int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc);
double integral =
(1.0/3)*(
A*(mbc-mac)+
(1.0/2)*B*(mbc*mbc-mac*mac)
)*(xc*xc*xc)+
(1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+
C*yb*xc+
(1.0/2)*B*yb*yb*xc;
vol += integral;
}
return vol;
}
double SMesh::CalculateSurfaceArea(const std::vector<uint32_t> &faces) const {
double area = 0.0;
for(uint32_t f : faces) {
for(const STriangle &t : l) {
if(f != t.meta.face) continue;
area += t.Area();
}
}
return area;
}

View File

@ -12,11 +12,11 @@
// Useful when splitting, tangent arcing, or removing bezier points. // Useful when splitting, tangent arcing, or removing bezier points.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) { void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) {
int i; for(auto &c : SK.constraint) {
for(i = 0; i < SK.constraint.n; i++) { if(c.ptA == oldpt)
Constraint *c = &(SK.constraint.elem[i]); c.ptA = newpt;
if(c->ptA.v == oldpt.v) c->ptA = newpt; if(c.ptB == oldpt)
if(c->ptB.v == oldpt.v) c->ptB = newpt; c.ptB = newpt;
} }
} }
@ -25,14 +25,13 @@ void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void GraphicsWindow::RemoveConstraintsForPointBeingDeleted(hEntity hpt) { void GraphicsWindow::RemoveConstraintsForPointBeingDeleted(hEntity hpt) {
SK.constraint.ClearTags(); SK.constraint.ClearTags();
for(int i = 0; i < SK.constraint.n; i++) { for(auto &c : SK.constraint) {
Constraint *c = &(SK.constraint.elem[i]); if(c.ptA == hpt || c.ptB == hpt) {
if(c->ptA.v == hpt.v || c->ptB.v == hpt.v) { c.tag = 1;
c->tag = 1;
(SS.deleted.constraints)++; (SS.deleted.constraints)++;
if(c->type != Constraint::Type::POINTS_COINCIDENT && if(c.type != Constraint::Type::POINTS_COINCIDENT &&
c->type != Constraint::Type::HORIZONTAL && c.type != Constraint::Type::HORIZONTAL &&
c->type != Constraint::Type::VERTICAL) c.type != Constraint::Type::VERTICAL)
{ {
(SS.deleted.nonTrivialConstraints)++; (SS.deleted.nonTrivialConstraints)++;
} }
@ -49,12 +48,12 @@ void GraphicsWindow::RemoveConstraintsForPointBeingDeleted(hEntity hpt) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) { void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
Request *r = SK.GetRequest(hr); Request *r = SK.GetRequest(hr);
if(r->group.v != SS.GW.activeGroup.v) return; if(r->group != SS.GW.activeGroup) return;
Entity *e; Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(!(e->h.isFromRequest())) continue; if(!(e->h.isFromRequest())) continue;
if(e->h.request().v != hr.v) continue; if(e->h.request() != hr) continue;
if(e->type != Entity::Type::POINT_IN_2D && if(e->type != Entity::Type::POINT_IN_2D &&
e->type != Entity::Type::POINT_IN_3D) e->type != Entity::Type::POINT_IN_3D)
@ -74,13 +73,13 @@ void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
SK.constraint.ClearTags(); SK.constraint.ClearTags();
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) { for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue; if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
if(c->group.v != SS.GW.activeGroup.v) continue; if(c->group != SS.GW.activeGroup) continue;
if(c->ptA.v == hpt.v) { if(c->ptA == hpt) {
ld.Add(&(c->ptB)); ld.Add(&(c->ptB));
c->tag = 1; c->tag = 1;
} }
if(c->ptB.v == hpt.v) { if(c->ptB == hpt) {
ld.Add(&(c->ptA)); ld.Add(&(c->ptA));
c->tag = 1; c->tag = 1;
} }
@ -97,9 +96,8 @@ void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
// those two points were implicitly coincident with each other. By // those two points were implicitly coincident with each other. By
// deleting hpt (and all constraints that mention it), we will delete // deleting hpt (and all constraints that mention it), we will delete
// that relationship. So put it back here now. // that relationship. So put it back here now.
int i; for(int i = 1; i < ld.n; i++) {
for(i = 1; i < ld.n; i++) { Constraint::ConstrainCoincident(ld[i-1], ld[i]);
Constraint::ConstrainCoincident(ld.elem[i-1], ld.elem[i]);
} }
ld.Clear(); ld.Clear();
} }
@ -233,10 +231,10 @@ void GraphicsWindow::ParametricCurve::ConstrainPointIfCoincident(hEntity hpt) {
ptv = pt->PointGetNum(); ptv = pt->PointGetNum();
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->h.v == pt->h.v) continue; if(e->h == pt->h) continue;
if(!e->IsPoint()) continue; if(!e->IsPoint()) continue;
if(e->group.v != pt->group.v) continue; if(e->group != pt->group) continue;
if(e->workplane.v != pt->workplane.v) continue; if(e->workplane != pt->workplane) continue;
ev = e->PointGetNum(); ev = e->PointGetNum();
if(!ev.Equals(ptv)) continue; if(!ev.Equals(ptv)) continue;
@ -270,18 +268,18 @@ void GraphicsWindow::MakeTangentArc() {
hRequest hreq[2]; hRequest hreq[2];
hEntity hent[2]; hEntity hent[2];
bool pointf[2]; bool pointf[2];
for(i = 0; i < SK.request.n; i++) { for(auto &r : SK.request) {
Request *r = &(SK.request.elem[i]); if(r.group != activeGroup)
if(r->group.v != activeGroup.v) continue; continue;
if(r->workplane.v != ActiveWorkplane().v) continue; if(r.workplane != ActiveWorkplane())
if(r->construction) continue; continue;
if(r->type != Request::Type::LINE_SEGMENT && if(r.construction)
r->type != Request::Type::ARC_OF_CIRCLE) continue;
{ if(r.type != Request::Type::LINE_SEGMENT && r.type != Request::Type::ARC_OF_CIRCLE) {
continue; continue;
} }
Entity *e = SK.GetEntity(r->h.entity(0)); Entity *e = SK.GetEntity(r.h.entity(0));
Vector ps = e->EndpointStart(), Vector ps = e->EndpointStart(),
pf = e->EndpointFinish(); pf = e->EndpointFinish();
@ -292,8 +290,8 @@ void GraphicsWindow::MakeTangentArc() {
// finish of this entity. // finish of this entity.
ent[c] = e; ent[c] = e;
hent[c] = e->h; hent[c] = e->h;
req[c] = r; req[c] = &r;
hreq[c] = r->h; hreq[c] = r.h;
pointf[c] = (pf.Equals(pshared)); pointf[c] = (pf.Equals(pshared));
} }
c++; c++;
@ -375,8 +373,8 @@ void GraphicsWindow::MakeTangentArc() {
tp[1] = t[1]; tp[1] = t[1];
// And convert those points to parameter values along the curve. // And convert those points to parameter values along the curve.
t[0] += (pa0.Minus(p0)).DivPivoting(t0); t[0] += (pa0.Minus(p0)).DivProjected(t0);
t[1] += (pa1.Minus(p1)).DivPivoting(t1); t[1] += (pa1.Minus(p1)).DivProjected(t1);
} }
// Stupid check for convergence, and for an out of range result (as // Stupid check for convergence, and for an out of range result (as
@ -411,9 +409,9 @@ void GraphicsWindow::MakeTangentArc() {
// Delete the coincident constraint for the removed point. // Delete the coincident constraint for the removed point.
SK.constraint.ClearTags(); SK.constraint.ClearTags();
for(i = 0; i < SK.constraint.n; i++) { for(i = 0; i < SK.constraint.n; i++) {
Constraint *cs = &(SK.constraint.elem[i]); Constraint *cs = &(SK.constraint[i]);
if(cs->group.v != activeGroup.v) continue; if(cs->group != activeGroup) continue;
if(cs->workplane.v != ActiveWorkplane().v) continue; if(cs->workplane != ActiveWorkplane()) continue;
if(cs->type != Constraint::Type::POINTS_COINCIDENT) continue; if(cs->type != Constraint::Type::POINTS_COINCIDENT) continue;
if (SK.GetEntity(cs->ptA)->PointGetNum().Equals(pshared)) { if (SK.GetEntity(cs->ptA)->PointGetNum().Equals(pshared)) {
cs->tag = 1; cs->tag = 1;
@ -536,7 +534,7 @@ hEntity GraphicsWindow::SplitCubic(hEntity he, Vector pinter) {
double t; double t;
int i, j; int i, j;
for(i = 0; i < sbl.l.n; i++) { for(i = 0; i < sbl.l.n; i++) {
SBezier *sb = &(sbl.l.elem[i]); SBezier *sb = &(sbl.l[i]);
ssassert(sb->deg == 3, "Expected a cubic bezier"); ssassert(sb->deg == 3, "Expected a cubic bezier");
sb->ClosestPointTo(pinter, &t, /*mustConverge=*/false); sb->ClosestPointTo(pinter, &t, /*mustConverge=*/false);
@ -603,15 +601,16 @@ hEntity GraphicsWindow::SplitEntity(hEntity he, Vector pinter) {
// Finally, delete the request that generated the original entity. // Finally, delete the request that generated the original entity.
Request::Type reqType = EntReqTable::GetRequestForEntity(entityType); Request::Type reqType = EntReqTable::GetRequestForEntity(entityType);
SK.request.ClearTags(); SK.request.ClearTags();
for(int i = 0; i < SK.request.n; i++) { for(auto &r : SK.request) {
Request *r = &(SK.request.elem[i]); if(r.group != activeGroup)
if(r->group.v != activeGroup.v) continue; continue;
if(r->type != reqType) continue; if(r.type != reqType)
continue;
// If the user wants to keep the old entities around, they can just // If the user wants to keep the old entities around, they can just
// mark them construction first. // mark them construction first.
if(he.v == r->h.entity(0).v && !r->construction) { if(he == r.h.entity(0) && !r.construction) {
r->tag = 1; r.tag = 1;
break; break;
} }
} }
@ -659,8 +658,8 @@ void GraphicsWindow::SplitLinesOrCurves() {
} }
for(Constraint &c : SK.constraint) { for(Constraint &c : SK.constraint) {
if(c.ptA.request().v == hb.request().v && if(c.ptA.request() == hb.request() &&
c.entityA.request().v == ha.request().v) { c.entityA.request() == ha.request()) {
pi = SK.GetEntity(c.ptA)->PointGetNum(); pi = SK.GetEntity(c.ptA)->PointGetNum();
if(ea->type == Entity::Type::LINE_SEGMENT && !pi.OnLineSegment(p0, p1)) { if(ea->type == Entity::Type::LINE_SEGMENT && !pi.OnLineSegment(p0, p1)) {
@ -682,7 +681,7 @@ void GraphicsWindow::SplitLinesOrCurves() {
sbla.AllIntersectionsWith(&sblb, &inters); sbla.AllIntersectionsWith(&sblb, &inters);
// If there's multiple points, then take the one closest to the mouse pointer. // If there's multiple points, then take the one closest to the mouse pointer.
if(inters.l.n > 0) { if(!inters.l.IsEmpty()) {
double dmin = VERY_POSITIVE; double dmin = VERY_POSITIVE;
SPoint *sp; SPoint *sp;
for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) { for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) {
@ -713,7 +712,7 @@ void GraphicsWindow::SplitLinesOrCurves() {
// Remove datum point, as it has now been superseded by the split point. // Remove datum point, as it has now been superseded by the split point.
SK.request.ClearTags(); SK.request.ClearTags();
for(Request &r : SK.request) { for(Request &r : SK.request) {
if(r.h.v == hb.request().v) { if(r.h == hb.request()) {
if(r.type == Request::Type::DATUM_POINT) { if(r.type == Request::Type::DATUM_POINT) {
// Delete datum point. // Delete datum point.
r.tag = 1; r.tag = 1;

View File

@ -24,7 +24,7 @@ void GraphicsWindow::AddPointToDraggedList(hEntity hp) {
// twice as far as the mouse pointer... // twice as far as the mouse pointer...
List<hEntity> *lhe = &(pending.points); List<hEntity> *lhe = &(pending.points);
for(hEntity *hee = lhe->First(); hee; hee = lhe->NextAfter(hee)) { for(hEntity *hee = lhe->First(); hee; hee = lhe->NextAfter(hee)) {
if(hee->v == hp.v) { if(*hee == hp) {
// Exact same point. // Exact same point.
return; return;
} }
@ -32,7 +32,7 @@ void GraphicsWindow::AddPointToDraggedList(hEntity hp) {
if(pe->type == p->type && if(pe->type == p->type &&
pe->type != Entity::Type::POINT_IN_2D && pe->type != Entity::Type::POINT_IN_2D &&
pe->type != Entity::Type::POINT_IN_3D && pe->type != Entity::Type::POINT_IN_3D &&
pe->group.v == p->group.v) pe->group == p->group)
{ {
// Transform-type point, from the same group. So it handles the // Transform-type point, from the same group. So it handles the
// same unknowns. // same unknowns.
@ -75,7 +75,7 @@ void GraphicsWindow::StartDraggingBySelection() {
// the hovered item too, and they'll always have it. // the hovered item too, and they'll always have it.
if(hover.entity.v) { if(hover.entity.v) {
hEntity dragEntity = ChooseFromHoverToDrag().entity; hEntity dragEntity = ChooseFromHoverToDrag().entity;
if(dragEntity.v != Entity::NO_ENTITY.v) { if(dragEntity != Entity::NO_ENTITY) {
StartDraggingByEntity(dragEntity); StartDraggingByEntity(dragEntity);
} }
} }
@ -383,7 +383,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
HitTestMakeSelection(mp); HitTestMakeSelection(mp);
hRequest hr = pending.point.request(); hRequest hr = pending.point.request();
if(pending.point.v == hr.entity(4).v) { if(pending.point == hr.entity(4)) {
// The very first segment; dragging final point drags both // The very first segment; dragging final point drags both
// tangent points. // tangent points.
Vector p0 = SK.GetEntity(hr.entity(1))->PointGetNum(), Vector p0 = SK.GetEntity(hr.entity(1))->PointGetNum(),
@ -486,7 +486,7 @@ void GraphicsWindow::ClearPending(bool scheduleShowTW) {
bool GraphicsWindow::IsFromPending(hRequest r) { bool GraphicsWindow::IsFromPending(hRequest r) {
for(auto &req : pending.requests) { for(auto &req : pending.requests) {
if(req.v == r.v) return true; if(req == r) return true;
} }
return false; return false;
} }
@ -497,8 +497,8 @@ void GraphicsWindow::AddToPending(hRequest r) {
void GraphicsWindow::ReplacePending(hRequest before, hRequest after) { void GraphicsWindow::ReplacePending(hRequest before, hRequest after) {
for(auto &req : pending.requests) { for(auto &req : pending.requests) {
if(req.v == before.v) { if(req == before) {
req.v = after.v; req = after;
} }
} }
} }
@ -721,7 +721,7 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
IdList<Constraint,hConstraint> *lc = &(SK.constraint); IdList<Constraint,hConstraint> *lc = &(SK.constraint);
for(c = lc->First(); c; c = lc->NextAfter(c)) { for(c = lc->First(); c; c = lc->NextAfter(c)) {
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue; if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) { if(c->ptA == p->h || c->ptB == p->h) {
break; break;
} }
} }
@ -734,7 +734,7 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
Constraint *c; Constraint *c;
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) { for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue; if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) { if(c->ptA == p->h || c->ptB == p->h) {
c->tag = 1; c->tag = 1;
} }
} }
@ -755,7 +755,7 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
[&]() { MenuEdit(Command::SELECT_ALL); }); [&]() { MenuEdit(Command::SELECT_ALL); });
} }
if((SS.clipboard.r.n > 0 || SS.clipboard.c.n > 0) && LockedInWorkplane()) { if((!SS.clipboard.r.IsEmpty() || !SS.clipboard.c.IsEmpty()) && LockedInWorkplane()) {
menu->AddItem(_("Paste"), menu->AddItem(_("Paste"),
[&]() { MenuClipboard(Command::PASTE); }); [&]() { MenuClipboard(Command::PASTE); });
menu->AddItem(_("Paste Transformed..."), menu->AddItem(_("Paste Transformed..."),
@ -1178,7 +1178,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my, bool shiftDown, bool ct
hRequest hr = pending.point.request(); hRequest hr = pending.point.request();
Request *r = SK.GetRequest(hr); Request *r = SK.GetRequest(hr);
if(hover.entity.v == hr.entity(1).v && r->extraPoints >= 2) { if(hover.entity == hr.entity(1) && r->extraPoints >= 2) {
// They want the endpoints coincident, which means a periodic // They want the endpoints coincident, which means a periodic
// spline instead. // spline instead.
r->type = Request::Type::CUBIC_PERIODIC; r->type = Request::Type::CUBIC_PERIODIC;
@ -1543,7 +1543,7 @@ void GraphicsWindow::SixDofEvent(Platform::SixDofEvent event) {
// point. // point.
int64_t now = GetMilliseconds(); int64_t now = GetMilliseconds();
if(now - last6DofTime > 5000 || if(now - last6DofTime > 5000 ||
last6DofGroup.v != g->h.v) last6DofGroup != g->h)
{ {
SS.UndoRemember(); SS.UndoRemember();
} }

View File

@ -312,7 +312,7 @@ static bool RunCommand(const std::vector<std::string> args) {
return false; return false;
} }
if(inputFiles.size() == 0) { if(inputFiles.empty()) {
fprintf(stderr, "At least one input file must be specified.\n"); fprintf(stderr, "At least one input file must be specified.\n");
return false; return false;
} }

View File

@ -29,8 +29,8 @@ int main(int argc, char** argv) {
Platform::Close3DConnexion(); Platform::Close3DConnexion();
SS.Clear(); SS.Clear();
SK.Clear(); SK.Clear();
Platform::ClearGui();
return 0; return 0;
} }

View File

@ -95,6 +95,7 @@ std::vector<FileFilter> MeshFileFilters = {
{ CN_("file-type", "Three.js-compatible mesh, with viewer"), { "html" } }, { CN_("file-type", "Three.js-compatible mesh, with viewer"), { "html" } },
{ CN_("file-type", "Three.js-compatible mesh, mesh only"), { "js" } }, { CN_("file-type", "Three.js-compatible mesh, mesh only"), { "js" } },
{ CN_("file-type", "Q3D Object file"), { "q3do" } }, { CN_("file-type", "Q3D Object file"), { "q3do" } },
{ CN_("file-type", "VRML text file"), { "wrl" } },
}; };
std::vector<FileFilter> SurfaceFileFilters = { std::vector<FileFilter> SurfaceFileFilters = {

View File

@ -107,7 +107,7 @@ void FatalError(std::string message);
// A native settings store. // A native settings store.
class Settings { class Settings {
public: public:
virtual ~Settings() {} virtual ~Settings() = default;
virtual void FreezeInt(const std::string &key, uint32_t value) = 0; virtual void FreezeInt(const std::string &key, uint32_t value) = 0;
virtual uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) = 0; virtual uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) = 0;
@ -135,7 +135,7 @@ class Timer {
public: public:
std::function<void()> onTimeout; std::function<void()> onTimeout;
virtual ~Timer() {} virtual ~Timer() = default;
virtual void RunAfter(unsigned milliseconds) = 0; virtual void RunAfter(unsigned milliseconds) = 0;
virtual void RunAfterNextFrame() { RunAfter(1); } virtual void RunAfterNextFrame() { RunAfter(1); }
@ -157,7 +157,7 @@ public:
std::function<void()> onTrigger; std::function<void()> onTrigger;
virtual ~MenuItem() {} virtual ~MenuItem() = default;
virtual void SetAccelerator(KeyboardEvent accel) = 0; virtual void SetAccelerator(KeyboardEvent accel) = 0;
virtual void SetIndicator(Indicator type) = 0; virtual void SetIndicator(Indicator type) = 0;
@ -170,7 +170,7 @@ typedef std::shared_ptr<MenuItem> MenuItemRef;
// A native menu. // A native menu.
class Menu { class Menu {
public: public:
virtual ~Menu() {} virtual ~Menu() = default;
virtual std::shared_ptr<MenuItem> AddItem( virtual std::shared_ptr<MenuItem> AddItem(
const std::string &label, std::function<void()> onTrigger = std::function<void()>(), const std::string &label, std::function<void()> onTrigger = std::function<void()>(),
@ -188,7 +188,7 @@ typedef std::shared_ptr<Menu> MenuRef;
// A native menu bar. // A native menu bar.
class MenuBar { class MenuBar {
public: public:
virtual ~MenuBar() {} virtual ~MenuBar() = default;
virtual std::shared_ptr<Menu> AddSubMenu(const std::string &label) = 0; virtual std::shared_ptr<Menu> AddSubMenu(const std::string &label) = 0;
@ -222,7 +222,7 @@ public:
std::function<void(double)> onScrollbarAdjusted; std::function<void(double)> onScrollbarAdjusted;
std::function<void()> onRender; std::function<void()> onRender;
virtual ~Window() {} virtual ~Window() = default;
// Returns physical display DPI. // Returns physical display DPI.
virtual double GetPixelDensity() = 0; virtual double GetPixelDensity() = 0;
@ -299,7 +299,7 @@ public:
std::function<void(Response)> onResponse; std::function<void(Response)> onResponse;
virtual ~MessageDialog() {} virtual ~MessageDialog() = default;
virtual void SetType(Type type) = 0; virtual void SetType(Type type) = 0;
virtual void SetTitle(std::string title) = 0; virtual void SetTitle(std::string title) = 0;
@ -347,7 +347,7 @@ extern std::vector<FileFilter> CsvFileFilters;
// A native dialog that asks to choose a file. // A native dialog that asks to choose a file.
class FileDialog { class FileDialog {
public: public:
virtual ~FileDialog() {} virtual ~FileDialog() = default;
virtual void SetTitle(std::string title) = 0; virtual void SetTitle(std::string title) = 0;
virtual void SetCurrentName(std::string name) = 0; virtual void SetCurrentName(std::string name) = 0;
@ -380,6 +380,7 @@ void OpenInBrowser(const std::string &url);
void InitGui(int argc, char **argv); void InitGui(int argc, char **argv);
void RunGui(); void RunGui();
void ExitGui(); void ExitGui();
void ClearGui();
} }

View File

@ -124,7 +124,7 @@ public:
} }
} }
~SettingsImplGtk() { ~SettingsImplGtk() override {
if(!_path.IsEmpty()) { if(!_path.IsEmpty()) {
// json-c <0.12 has the first argument non-const // json-c <0.12 has the first argument non-const
if(json_object_to_file_ext((char *)_path.raw.c_str(), _json, if(json_object_to_file_ext((char *)_path.raw.c_str(), _json,
@ -1292,7 +1292,8 @@ public:
void FilterChanged() { void FilterChanged() {
std::string extension = GetExtension(); std::string extension = GetExtension();
if(extension == "") return; if(extension.empty())
return;
Platform::Path path = GetFilename(); Platform::Path path = GetFilename();
SetCurrentName(path.WithExtension(extension).FileName()); SetCurrentName(path.WithExtension(extension).FileName());
@ -1470,11 +1471,14 @@ void InitGui(int argc, char **argv) {
} }
void RunGui() { void RunGui() {
gtkMain->run(); Gtk::Main::run();
} }
void ExitGui() { void ExitGui() {
gtkMain->quit(); Gtk::Main::quit();
}
void ClearGui() {
delete gtkMain; delete gtkMain;
} }

View File

@ -1479,5 +1479,7 @@ void ExitGui() {
[NSApp terminate:nil]; [NSApp terminate:nil];
} }
void ClearGui() {}
} }
} }

View File

@ -53,22 +53,24 @@ void FatalError(std::string message) {
class SettingsImplDummy final : public Settings { class SettingsImplDummy final : public Settings {
public: public:
void FreezeInt(const std::string &key, uint32_t value) {} void FreezeInt(const std::string &key, uint32_t value) override {
}
uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) { uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) override {
return defaultValue; return defaultValue;
} }
void FreezeFloat(const std::string &key, double value) {} void FreezeFloat(const std::string &key, double value) override {
}
double ThawFloat(const std::string &key, double defaultValue = 0.0) { double ThawFloat(const std::string &key, double defaultValue = 0.0) override {
return defaultValue; return defaultValue;
} }
void FreezeString(const std::string &key, const std::string &value) {} void FreezeString(const std::string &key, const std::string &value) override {
}
std::string ThawString(const std::string &key, std::string ThawString(const std::string &key, const std::string &defaultValue = "") override {
const std::string &defaultValue = "") {
return defaultValue; return defaultValue;
} }
}; };
@ -154,6 +156,8 @@ void ExitGui() {
exit(0); exit(0);
} }
void ClearGui() {}
} }
} }

View File

@ -1369,7 +1369,9 @@ public:
} }
}; };
#if HAVE_OPENGL == 3
EGLDisplay WindowImplWin32::eglDisplay = EGL_NO_DISPLAY; EGLDisplay WindowImplWin32::eglDisplay = EGL_NO_DISPLAY;
#endif
WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) { WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) {
return std::make_shared<WindowImplWin32>(kind, return std::make_shared<WindowImplWin32>(kind,
@ -1675,5 +1677,7 @@ void ExitGui() {
PostQuitMessage(0); PostQuitMessage(0);
} }
void ClearGui() {}
} }
} }

View File

@ -203,7 +203,7 @@ static void FindPrefix(const std::string &raw, size_t *pos) {
} }
} }
#else #else
if(raw.size() >= 1 && raw[0] == '/') { if(!raw.empty() && raw[0] == '/') {
*pos = 1; *pos = 1;
} }
#endif #endif
@ -295,7 +295,7 @@ Path Path::Expand(bool fromCurrentDirectory) const {
if(expanded.IsEmpty()) { if(expanded.IsEmpty()) {
if(expandedComponents.empty()) { if(expandedComponents.empty()) {
expandedComponents.push_back("."); expandedComponents.emplace_back(".");
} }
expanded = From(Concat(expandedComponents, SEPARATOR)); expanded = From(Concat(expandedComponents, SEPARATOR));
} else if(!expandedComponents.empty()) { } else if(!expandedComponents.empty()) {
@ -371,12 +371,12 @@ Path Path::RelativeTo(const Path &base) const {
std::vector<std::string> resultComponents; std::vector<std::string> resultComponents;
for(size_t i = common; i < baseComponents.size(); i++) { for(size_t i = common; i < baseComponents.size(); i++) {
resultComponents.push_back(".."); resultComponents.emplace_back("..");
} }
resultComponents.insert(resultComponents.end(), resultComponents.insert(resultComponents.end(),
components.begin() + common, components.end()); components.begin() + common, components.end());
if(resultComponents.empty()) { if(resultComponents.empty()) {
resultComponents.push_back("."); resultComponents.emplace_back(".");
} }
return From(Concat(resultComponents, SEPARATOR)); return From(Concat(resultComponents, SEPARATOR));
} }

View File

@ -54,8 +54,7 @@ void *AllocTemporary(size_t n)
return (void *)&h[1]; return (void *)&h[1];
} }
void FreeAllTemporary(void) void FreeAllTemporary() {
{
AllocTempHeader *h = Head; AllocTempHeader *h = Head;
while(h) { while(h) {
AllocTempHeader *f = h; AllocTempHeader *f = h;
@ -77,8 +76,9 @@ void MemFree(void *p) {
std::vector<std::string> InitPlatform(int argc, char **argv) { std::vector<std::string> InitPlatform(int argc, char **argv) {
std::vector<std::string> args; std::vector<std::string> args;
args.reserve(argc);
for(int i = 0; i < argc; i++) { for(int i = 0; i < argc; i++) {
args.push_back(argv[i]); args.emplace_back(argv[i]);
} }
return args; return args;
} }

View File

@ -87,6 +87,12 @@ double STriangle::SignedVolume() const {
return a.Dot(b.Cross(c)) / 6.0; return a.Dot(b.Cross(c)) / 6.0;
} }
double STriangle::Area() const {
Vector ab = a.Minus(b);
Vector cb = c.Minus(b);
return ab.Cross(cb).Magnitude() / 2.0;
}
bool STriangle::IsDegenerate() const { bool STriangle::IsDegenerate() const {
return a.OnLineSegment(b, c) || return a.OnLineSegment(b, c) ||
b.OnLineSegment(a, c) || b.OnLineSegment(a, c) ||
@ -158,13 +164,13 @@ bool SEdge::EdgeCrosses(Vector ea, Vector eb, Vector *ppi, SPointList *spl) cons
// on the other // on the other
bool inters = false; bool inters = false;
double t; double t;
t = a.Minus(ea).DivPivoting(d); t = a.Minus(ea).DivProjected(d);
if(t > t_eps && t < (1 - t_eps)) inters = true; if(t > t_eps && t < (1 - t_eps)) inters = true;
t = b.Minus(ea).DivPivoting(d); t = b.Minus(ea).DivProjected(d);
if(t > t_eps && t < (1 - t_eps)) inters = true; if(t > t_eps && t < (1 - t_eps)) inters = true;
t = ea.Minus(a).DivPivoting(dthis); t = ea.Minus(a).DivProjected(dthis);
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true; if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
t = eb.Minus(a).DivPivoting(dthis); t = eb.Minus(a).DivProjected(dthis);
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true; if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
if(inters) { if(inters) {
@ -227,7 +233,8 @@ bool SEdgeList::AssembleContour(Vector first, Vector last, SContour *dest,
do { do {
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
SEdge *se = &(l.elem[i]); /// @todo fix const!
SEdge *se = const_cast<SEdge*>(&(l[i]));
if(se->tag) continue; if(se->tag) continue;
if(se->a.Equals(last)) { if(se->a.Equals(last)) {
@ -267,10 +274,11 @@ bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) co
Vector last = Vector::From(0, 0, 0); Vector last = Vector::From(0, 0, 0);
int i; int i;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
if(!l.elem[i].tag) { if(!l[i].tag) {
first = l.elem[i].a; first = l[i].a;
last = l.elem[i].b; last = l[i].b;
l.elem[i].tag = 1; /// @todo fix const!
const_cast<SEdge*>(&(l[i]))->tag = 1;
break; break;
} }
} }
@ -281,9 +289,7 @@ bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) co
// Create a new empty contour in our polygon, and finish assembling // Create a new empty contour in our polygon, and finish assembling
// into that contour. // into that contour.
dest->AddEmptyContour(); dest->AddEmptyContour();
if(!AssembleContour(first, last, &(dest->l.elem[dest->l.n-1]), if(!AssembleContour(first, last, dest->l.Last(), errorAt, keepDir)) {
errorAt, keepDir))
{
allClosed = false; allClosed = false;
} }
// But continue assembling, even if some of the contours are open // But continue assembling, even if some of the contours are open
@ -335,11 +341,10 @@ bool SEdgeList::ContainsEdge(const SEdge *set) const {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SEdgeList::CullExtraneousEdges(bool both) { void SEdgeList::CullExtraneousEdges(bool both) {
l.ClearTags(); l.ClearTags();
int i, j; for(int i = 0; i < l.n; i++) {
for(i = 0; i < l.n; i++) { SEdge *se = &(l[i]);
SEdge *se = &(l.elem[i]); for(int j = i + 1; j < l.n; j++) {
for(j = i+1; j < l.n; j++) { SEdge *set = &(l[j]);
SEdge *set = &(l.elem[j]);
if((set->a).Equals(se->a) && (set->b).Equals(se->b)) { if((set->a).Equals(se->a) && (set->b).Equals(se->b)) {
// Two parallel edges exist; so keep only the first one. // Two parallel edges exist; so keep only the first one.
set->tag = 1; set->tag = 1;
@ -491,33 +496,27 @@ int SKdNodeEdges::AnyEdgeCrossings(Vector a, Vector b, int cnt,
// We have an edge list that contains only collinear edges, maybe with more // We have an edge list that contains only collinear edges, maybe with more
// splits than necessary. Merge any collinear segments that join. // splits than necessary. Merge any collinear segments that join.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static Vector LineStart, LineDirection;
static int ByTAlongLine(const void *av, const void *bv)
{
SEdge *a = (SEdge *)av,
*b = (SEdge *)bv;
double ta = (a->a.Minus(LineStart)).DivPivoting(LineDirection),
tb = (b->a.Minus(LineStart)).DivPivoting(LineDirection);
return (ta > tb) ? 1 : -1;
}
void SEdgeList::MergeCollinearSegments(Vector a, Vector b) { void SEdgeList::MergeCollinearSegments(Vector a, Vector b) {
LineStart = a; const Vector lineStart = a;
LineDirection = b.Minus(a); const Vector lineDirection = b.Minus(a);
qsort(l.elem, l.n, sizeof(l.elem[0]), ByTAlongLine); std::sort(l.begin(), l.end(), [&](const SEdge &a, const SEdge &b) {
double ta = (a.a.Minus(lineStart)).DivProjected(lineDirection);
double tb = (b.a.Minus(lineStart)).DivProjected(lineDirection);
return (ta < tb);
});
l.ClearTags(); l.ClearTags();
int i; SEdge *prev = nullptr;
for(i = 1; i < l.n; i++) { for(auto &now : l) {
SEdge *prev = &(l.elem[i-1]), if(prev != nullptr) {
*now = &(l.elem[i]); if((prev->b).Equals(now.a) && prev->auxA == now.auxA) {
// The previous segment joins up to us; so merge it into us.
if((prev->b).Equals(now->a) && prev->auxA == now->auxA) { prev->tag = 1;
// The previous segment joins up to us; so merge it into us. now.a = prev->a;
prev->tag = 1; }
now->a = prev->a;
} }
prev = &now;
} }
l.RemoveTagged(); l.RemoveTagged();
} }
@ -533,7 +532,7 @@ bool SPointList::ContainsPoint(Vector pt) const {
int SPointList::IndexForPoint(Vector pt) const { int SPointList::IndexForPoint(Vector pt) const {
int i; int i;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
SPoint *p = &(l.elem[i]); const SPoint *p = &(l[i]);
if(pt.Equals(p->p)) { if(pt.Equals(p->p)) {
return i; return i;
} }
@ -573,7 +572,7 @@ void SContour::AddPoint(Vector p) {
void SContour::MakeEdgesInto(SEdgeList *el) const { void SContour::MakeEdgesInto(SEdgeList *el) const {
int i; int i;
for(i = 0; i < (l.n - 1); i++) { for(i = 0; i < (l.n - 1); i++) {
el->AddEdge(l.elem[i].p, l.elem[i+1].p); el->AddEdge(l[i].p, l[i+1].p);
} }
} }
@ -596,8 +595,8 @@ Vector SContour::ComputeNormal() const {
Vector n = Vector::From(0, 0, 0); Vector n = Vector::From(0, 0, 0);
for(int i = 0; i < l.n - 2; i++) { for(int i = 0; i < l.n - 2; i++) {
Vector u = (l.elem[i+1].p).Minus(l.elem[i+0].p).WithMagnitude(1); Vector u = (l[i+1].p).Minus(l[i+0].p).WithMagnitude(1);
Vector v = (l.elem[i+2].p).Minus(l.elem[i+1].p).WithMagnitude(1); Vector v = (l[i+2].p).Minus(l[i+1].p).WithMagnitude(1);
Vector nt = u.Cross(v); Vector nt = u.Cross(v);
if(nt.Magnitude() > n.Magnitude()) { if(nt.Magnitude() > n.Magnitude()) {
n = nt; n = nt;
@ -608,7 +607,7 @@ Vector SContour::ComputeNormal() const {
Vector SContour::AnyEdgeMidpoint() const { Vector SContour::AnyEdgeMidpoint() const {
ssassert(l.n >= 2, "Need two points to find a midpoint"); ssassert(l.n >= 2, "Need two points to find a midpoint");
return ((l.elem[0].p).Plus(l.elem[1].p)).ScaledBy(0.5); return ((l[0].p).Plus(l[1].p)).ScaledBy(0.5);
} }
bool SContour::IsClockwiseProjdToNormal(Vector n) const { bool SContour::IsClockwiseProjdToNormal(Vector n) const {
@ -626,10 +625,10 @@ double SContour::SignedAreaProjdToNormal(Vector n) const {
double area = 0; double area = 0;
for(int i = 0; i < (l.n - 1); i++) { for(int i = 0; i < (l.n - 1); i++) {
double u0 = (l.elem[i ].p).Dot(u); double u0 = (l[i ].p).Dot(u);
double v0 = (l.elem[i ].p).Dot(v); double v0 = (l[i ].p).Dot(v);
double u1 = (l.elem[i+1].p).Dot(u); double u1 = (l[i+1].p).Dot(u);
double v1 = (l.elem[i+1].p).Dot(v); double v1 = (l[i+1].p).Dot(v);
area += ((v0 + v1)/2)*(u1 - u0); area += ((v0 + v1)/2)*(u1 - u0);
} }
@ -645,11 +644,11 @@ bool SContour::ContainsPointProjdToNormal(Vector n, Vector p) const {
bool inside = false; bool inside = false;
for(int i = 0; i < (l.n - 1); i++) { for(int i = 0; i < (l.n - 1); i++) {
double ua = (l.elem[i ].p).Dot(u); double ua = (l[i ].p).Dot(u);
double va = (l.elem[i ].p).Dot(v); double va = (l[i ].p).Dot(v);
// The curve needs to be exactly closed; approximation is death. // The curve needs to be exactly closed; approximation is death.
double ub = (l.elem[(i+1)%(l.n-1)].p).Dot(u); double ub = (l[(i+1)%(l.n-1)].p).Dot(u);
double vb = (l.elem[(i+1)%(l.n-1)].p).Dot(v); double vb = (l[(i+1)%(l.n-1)].p).Dot(v);
if ((((va <= vp) && (vp < vb)) || if ((((va <= vp) && (vp < vb)) ||
((vb <= vp) && (vp < va))) && ((vb <= vp) && (vp < va))) &&
@ -670,7 +669,7 @@ void SContour::Reverse() {
void SPolygon::Clear() { void SPolygon::Clear() {
int i; int i;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
(l.elem[i]).l.Clear(); (l[i]).l.Clear();
} }
l.Clear(); l.Clear();
} }
@ -683,13 +682,14 @@ void SPolygon::AddEmptyContour() {
void SPolygon::MakeEdgesInto(SEdgeList *el) const { void SPolygon::MakeEdgesInto(SEdgeList *el) const {
int i; int i;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
(l.elem[i]).MakeEdgesInto(el); (l[i]).MakeEdgesInto(el);
} }
} }
Vector SPolygon::ComputeNormal() const { Vector SPolygon::ComputeNormal() const {
if(l.n < 1) return Vector::From(0, 0, 0); if(l.IsEmpty())
return (l.elem[0]).ComputeNormal(); return Vector::From(0, 0, 0);
return (l[0]).ComputeNormal();
} }
double SPolygon::SignedArea() const { double SPolygon::SignedArea() const {
@ -710,7 +710,7 @@ int SPolygon::WindingNumberForPoint(Vector p) const {
int winding = 0; int winding = 0;
int i; int i;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
SContour *sc = &(l.elem[i]); const SContour *sc = &(l[i]);
if(sc->ContainsPointProjdToNormal(normal, p)) { if(sc->ContainsPointProjdToNormal(normal, p)) {
winding++; winding++;
} }
@ -725,18 +725,18 @@ void SPolygon::FixContourDirections() {
// Outside curve looks counterclockwise, projected against our normal. // Outside curve looks counterclockwise, projected against our normal.
int i, j; int i, j;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
SContour *sc = &(l.elem[i]); SContour *sc = &(l[i]);
if(sc->l.n < 2) continue; if(sc->l.n < 2) continue;
// The contours may not intersect, but they may share vertices; so // The contours may not intersect, but they may share vertices; so
// testing a vertex for point-in-polygon may fail, but the midpoint // testing a vertex for point-in-polygon may fail, but the midpoint
// of an edge is okay. // of an edge is okay.
Vector pt = (((sc->l.elem[0]).p).Plus(sc->l.elem[1].p)).ScaledBy(0.5); Vector pt = (((sc->l[0]).p).Plus(sc->l[1].p)).ScaledBy(0.5);
sc->timesEnclosed = 0; sc->timesEnclosed = 0;
bool outer = true; bool outer = true;
for(j = 0; j < l.n; j++) { for(j = 0; j < l.n; j++) {
if(i == j) continue; if(i == j) continue;
SContour *sct = &(l.elem[j]); SContour *sct = &(l[j]);
if(sct->ContainsPointProjdToNormal(normal, pt)) { if(sct->ContainsPointProjdToNormal(normal, pt)) {
outer = !outer; outer = !outer;
(sc->timesEnclosed)++; (sc->timesEnclosed)++;
@ -752,13 +752,14 @@ void SPolygon::FixContourDirections() {
} }
bool SPolygon::IsEmpty() const { bool SPolygon::IsEmpty() const {
if(l.n == 0 || l.elem[0].l.n == 0) return true; if(l.IsEmpty() || l[0].l.IsEmpty())
return true;
return false; return false;
} }
Vector SPolygon::AnyPoint() const { Vector SPolygon::AnyPoint() const {
ssassert(!IsEmpty(), "Need at least one point"); ssassert(!IsEmpty(), "Need at least one point");
return l.elem[0].l.elem[0].p; return l[0].l[0].p;
} }
bool SPolygon::SelfIntersecting(Vector *intersectsAt) const { bool SPolygon::SelfIntersecting(Vector *intersectsAt) const {
@ -804,9 +805,9 @@ void SPolygon::OffsetInto(SPolygon *dest, double r) const {
int i; int i;
dest->Clear(); dest->Clear();
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
SContour *sc = &(l.elem[i]); const SContour *sc = &(l[i]);
dest->AddEmptyContour(); dest->AddEmptyContour();
sc->OffsetInto(&(dest->l.elem[dest->l.n-1]), r); sc->OffsetInto(&(dest->l[dest->l.n-1]), r);
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -861,9 +862,9 @@ void SContour::OffsetInto(SContour *dest, double r) const {
Vector dp, dn; Vector dp, dn;
double thetan, thetap; double thetan, thetap;
a = l.elem[WRAP(i-1, (l.n-1))].p; a = l[WRAP(i-1, (l.n-1))].p;
b = l.elem[WRAP(i, (l.n-1))].p; b = l[WRAP(i, (l.n-1))].p;
c = l.elem[WRAP(i+1, (l.n-1))].p; c = l[WRAP(i+1, (l.n-1))].p;
dp = a.Minus(b); dp = a.Minus(b);
thetap = atan2(dp.y, dp.x); thetap = atan2(dp.y, dp.x);

View File

@ -191,6 +191,7 @@ public:
bool Raytrace(const Vector &rayPoint, const Vector &rayDir, bool Raytrace(const Vector &rayPoint, const Vector &rayDir,
double *t, Vector *inters) const; double *t, Vector *inters) const;
double SignedVolume() const; double SignedVolume() const;
double Area() const;
bool IsDegenerate() const; bool IsDegenerate() const;
}; };
@ -280,6 +281,8 @@ public:
void PrecomputeTransparency(); void PrecomputeTransparency();
void RemoveDegenerateTriangles(); void RemoveDegenerateTriangles();
double CalculateVolume() const;
double CalculateSurfaceArea(const std::vector<uint32_t> &faces) const;
bool IsEmpty() const; bool IsEmpty() const;
void RemapFaces(Group *g, int remap); void RemapFaces(Group *g, int remap);

View File

@ -244,7 +244,7 @@ MeshRenderer::Handle MeshRenderer::Add(const SMesh &m, bool dynamic) {
MeshVertex *vertices = new MeshVertex[m.l.n * 3]; MeshVertex *vertices = new MeshVertex[m.l.n * 3];
for(int i = 0; i < m.l.n; i++) { for(int i = 0; i < m.l.n; i++) {
const STriangle &t = m.l.elem[i]; const STriangle &t = m.l[i];
vertices[i * 3 + 0].pos = Vector3f::From(t.a); vertices[i * 3 + 0].pos = Vector3f::From(t.a);
vertices[i * 3 + 1].pos = Vector3f::From(t.b); vertices[i * 3 + 1].pos = Vector3f::From(t.b);
vertices[i * 3 + 2].pos = Vector3f::From(t.c); vertices[i * 3 + 2].pos = Vector3f::From(t.c);
@ -395,7 +395,7 @@ GLuint Generate(const std::vector<double> &pattern) {
int dashI = 0; int dashI = 0;
double dashT = 0.0; double dashT = 0.0;
for(int i = 0; i < size; i++) { for(int i = 0; i < size; i++) {
if(pattern.size() == 0) { if(pattern.empty()) {
textureData[i] = EncodeLengthAsFloat(0.0); textureData[i] = EncodeLengthAsFloat(0.0);
continue; continue;
} }
@ -485,8 +485,8 @@ EdgeRenderer::Handle EdgeRenderer::Add(const SEdgeList &edges, bool dynamic) {
uint32_t curVertex = 0; uint32_t curVertex = 0;
uint32_t curIndex = 0; uint32_t curIndex = 0;
for(int i = 0; i < edges.l.n; i++) { for(int i = 0; i < edges.l.n; i++) {
const SEdge &curr = edges.l.elem[i]; const SEdge &curr = edges.l[i];
const SEdge &next = edges.l.elem[(i + 1) % edges.l.n]; const SEdge &next = edges.l[(i + 1) % edges.l.n];
// 3d positions // 3d positions
Vector3f a = Vector3f::From(curr.a); Vector3f a = Vector3f::From(curr.a);
@ -674,8 +674,8 @@ OutlineRenderer::Handle OutlineRenderer::Add(const SOutlineList &outlines, bool
uint32_t curIndex = 0; uint32_t curIndex = 0;
for(int i = 0; i < outlines.l.n; i++) { for(int i = 0; i < outlines.l.n; i++) {
const SOutline &curr = outlines.l.elem[i]; const SOutline &curr = outlines.l[i];
const SOutline &next = outlines.l.elem[(i + 1) % outlines.l.n]; const SOutline &next = outlines.l[(i + 1) % outlines.l.n];
// 3d positions // 3d positions
Vector3f a = Vector3f::From(curr.a); Vector3f a = Vector3f::From(curr.a);

View File

@ -142,6 +142,7 @@ public:
BitmapFont bitmapFont = {}; BitmapFont bitmapFont = {};
virtual void Clear(); virtual void Clear();
virtual ~Canvas() = default;
hStroke GetStroke(const Stroke &stroke); hStroke GetStroke(const Stroke &stroke);
hFill GetFill(const Fill &fill); hFill GetFill(const Fill &fill);
@ -172,6 +173,13 @@ public:
virtual std::shared_ptr<BatchCanvas> CreateBatch(); virtual std::shared_ptr<BatchCanvas> CreateBatch();
}; };
template<>
struct IsHandleOracle<Canvas::hStroke> : std::true_type {};
template<>
struct IsHandleOracle<Canvas::hFill> : std::true_type {};
// An interface for view-dependent visualization. // An interface for view-dependent visualization.
class ViewportCanvas : public Canvas { class ViewportCanvas : public Canvas {
public: public:
@ -276,7 +284,7 @@ public:
const Camera &GetCamera() const override { return camera; } const Camera &GetCamera() const override { return camera; }
// ViewportCanvas interface. // ViewportCanvas interface.
void SetCamera(const Camera &camera) override { this->camera = camera; }; void SetCamera(const Camera &camera) override { this->camera = camera; }
void SetLighting(const Lighting &lighting) override { this->lighting = lighting; } void SetLighting(const Lighting &lighting) override { this->lighting = lighting; }
void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override; void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override;
@ -353,7 +361,7 @@ public:
void OutputEnd() override; void OutputEnd() override;
}; };
class CairoPixmapRenderer : public CairoRenderer { class CairoPixmapRenderer final : public CairoRenderer {
public: public:
std::shared_ptr<Pixmap> pixmap; std::shared_ptr<Pixmap> pixmap;

View File

@ -243,7 +243,7 @@ void SurfaceRenderer::ConvertBeziersToEdges() {
List<Vector> lv = {}; List<Vector> lv = {};
b.MakePwlInto(&lv, chordTolerance); b.MakePwlInto(&lv, chordTolerance);
for(int i = 1; i < lv.n; i++) { for(int i = 1; i < lv.n; i++) {
el.AddEdge(lv.elem[i-1], lv.elem[i]); el.AddEdge(lv[i-1], lv[i]);
} }
lv.Clear(); lv.Clear();
} }
@ -255,7 +255,8 @@ void SurfaceRenderer::ConvertBeziersToEdges() {
void SurfaceRenderer::CullOccludedStrokes() { void SurfaceRenderer::CullOccludedStrokes() {
// Perform occlusion testing, if necessary. // Perform occlusion testing, if necessary.
if(mesh.l.n == 0) return; if(mesh.l.IsEmpty())
return;
// We can't perform hidden line removal on exact curves. // We can't perform hidden line removal on exact curves.
ConvertBeziersToEdges(); ConvertBeziersToEdges();

View File

@ -58,7 +58,7 @@ void CairoRenderer::OutputEnd() {
} }
void CairoRenderer::SelectStroke(hStroke hcs) { void CairoRenderer::SelectStroke(hStroke hcs) {
if(current.hcs.v == hcs.v) return; if(current.hcs == hcs) return;
FinishPath(); FinishPath();
Stroke *stroke = strokes.FindById(hcs); Stroke *stroke = strokes.FindById(hcs);

View File

@ -249,7 +249,7 @@ void OpenGl1Renderer::UnSelectPrimitive() {
} }
Canvas::Stroke *OpenGl1Renderer::SelectStroke(hStroke hcs) { Canvas::Stroke *OpenGl1Renderer::SelectStroke(hStroke hcs) {
if(current.hcs.v == hcs.v) return current.stroke; if(current.hcs == hcs) return current.stroke;
Stroke *stroke = strokes.FindById(hcs); Stroke *stroke = strokes.FindById(hcs);
UnSelectPrimitive(); UnSelectPrimitive();
@ -270,7 +270,7 @@ Canvas::Stroke *OpenGl1Renderer::SelectStroke(hStroke hcs) {
} }
Canvas::Fill *OpenGl1Renderer::SelectFill(hFill hcf) { Canvas::Fill *OpenGl1Renderer::SelectFill(hFill hcf) {
if(current.hcf.v == hcf.v) return current.fill; if(current.hcf == hcf) return current.fill;
Fill *fill = fills.FindById(hcf); Fill *fill = fills.FindById(hcf);
UnSelectPrimitive(); UnSelectPrimitive();

View File

@ -195,7 +195,7 @@ static void ssglDepthRange(Canvas::Layer layer, int zIndex) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
Canvas::Stroke *OpenGl3Renderer::SelectStroke(hStroke hcs) { Canvas::Stroke *OpenGl3Renderer::SelectStroke(hStroke hcs) {
if(current.hcs.v == hcs.v) return current.stroke; if(current.hcs == hcs) return current.stroke;
Stroke *stroke = strokes.FindById(hcs); Stroke *stroke = strokes.FindById(hcs);
ssglDepthRange(stroke->layer, stroke->zIndex); ssglDepthRange(stroke->layer, stroke->zIndex);
@ -241,7 +241,7 @@ void OpenGl3Renderer::SelectMask(FillPattern pattern) {
} }
Canvas::Fill *OpenGl3Renderer::SelectFill(hFill hcf) { Canvas::Fill *OpenGl3Renderer::SelectFill(hFill hcf) {
if(current.hcf.v == hcf.v) return current.fill; if(current.hcf == hcf) return current.fill;
Fill *fill = fills.FindById(hcf); Fill *fill = fills.FindById(hcf);
ssglDepthRange(fill->layer, fill->zIndex); ssglDepthRange(fill->layer, fill->zIndex);
@ -458,7 +458,8 @@ void OpenGl3Renderer::DrawEdges(const SEdgeList &el, hStroke hcs) {
} }
void OpenGl3Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs mode) { void OpenGl3Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs mode) {
if(ol.l.n == 0) return; if(ol.l.IsEmpty())
return;
Stroke *stroke = SelectStroke(hcs); Stroke *stroke = SelectStroke(hcs);
ssassert(stroke->stipplePattern != StipplePattern::ZIGZAG && ssassert(stroke->stipplePattern != StipplePattern::ZIGZAG &&
@ -726,8 +727,8 @@ public:
// Data // Data
EdgeRenderer::Handle handle; EdgeRenderer::Handle handle;
virtual Canvas::Layer GetLayer() const override { return stroke.layer; }; Canvas::Layer GetLayer() const override { return stroke.layer; }
virtual int GetZIndex() const override { return stroke.zIndex; }; int GetZIndex() const override { return stroke.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SEdgeList &el, static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SEdgeList &el,
Canvas::Stroke *stroke) { Canvas::Stroke *stroke) {
@ -756,8 +757,8 @@ public:
OutlineRenderer::Handle handle; OutlineRenderer::Handle handle;
Canvas::DrawOutlinesAs drawAs; Canvas::DrawOutlinesAs drawAs;
virtual Canvas::Layer GetLayer() const override { return stroke.layer; }; Canvas::Layer GetLayer() const override { return stroke.layer; }
virtual int GetZIndex() const override { return stroke.zIndex; }; int GetZIndex() const override { return stroke.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SOutlineList &ol, static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SOutlineList &ol,
Canvas::Stroke *stroke, Canvas::Stroke *stroke,
@ -787,8 +788,8 @@ public:
// Data // Data
IndexedMeshRenderer::Handle handle; IndexedMeshRenderer::Handle handle;
virtual Canvas::Layer GetLayer() const override { return stroke.layer; }; Canvas::Layer GetLayer() const override { return stroke.layer; }
virtual int GetZIndex() const override { return stroke.zIndex; }; int GetZIndex() const override { return stroke.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SIndexedMesh &mesh, static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SIndexedMesh &mesh,
Canvas::Stroke *stroke) { Canvas::Stroke *stroke) {
@ -816,8 +817,8 @@ public:
// Data // Data
IndexedMeshRenderer::Handle handle; IndexedMeshRenderer::Handle handle;
virtual Canvas::Layer GetLayer() const override { return fill.layer; }; Canvas::Layer GetLayer() const override { return fill.layer; }
virtual int GetZIndex() const override { return fill.zIndex; }; int GetZIndex() const override { return fill.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SIndexedMesh &mesh, static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SIndexedMesh &mesh,
Canvas::Fill *fill) { Canvas::Fill *fill) {
@ -855,8 +856,8 @@ public:
bool hasFillBack; bool hasFillBack;
bool isShaded; bool isShaded;
virtual Canvas::Layer GetLayer() const override { return fillFront.layer; }; Canvas::Layer GetLayer() const override { return fillFront.layer; }
virtual int GetZIndex() const override { return fillFront.zIndex; }; int GetZIndex() const override { return fillFront.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SMesh &m, static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SMesh &m,
Canvas::Fill *fillFront, Canvas::Fill *fillBack = NULL, Canvas::Fill *fillFront, Canvas::Fill *fillBack = NULL,
@ -1020,7 +1021,7 @@ public:
void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack = {}) override { void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack = {}) override {
drawCalls.emplace(MeshDrawCall::Create(renderer, m, fills.FindById(hcfFront), drawCalls.emplace(MeshDrawCall::Create(renderer, m, fills.FindById(hcfFront),
fills.FindByIdNoOops(hcfBack), fills.FindByIdNoOops(hcfBack),
/*lighting=*/true)); /*isShaded=*/true));
} }
void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) override { void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) override {

View File

@ -146,7 +146,7 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
p.group = group; p.group = group;
p.style = style; p.style = style;
p.construction = e.construction; p.construction = e.construction;
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane == Entity::FREE_IN_3D) {
p.type = Entity::Type::POINT_IN_3D; p.type = Entity::Type::POINT_IN_3D;
// params for x y z // params for x y z
p.param[0] = AddParam(param, h.param(16 + 3*i + 0)); p.param[0] = AddParam(param, h.param(16 + 3*i + 0));
@ -168,7 +168,7 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
n.group = group; n.group = group;
n.style = style; n.style = style;
n.construction = e.construction; n.construction = e.construction;
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane == Entity::FREE_IN_3D) {
n.type = Entity::Type::NORMAL_IN_3D; n.type = Entity::Type::NORMAL_IN_3D;
n.param[0] = AddParam(param, h.param(32+0)); n.param[0] = AddParam(param, h.param(32+0));
n.param[1] = AddParam(param, h.param(32+1)); n.param[1] = AddParam(param, h.param(32+1));
@ -203,11 +203,11 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
std::string Request::DescriptionString() const { std::string Request::DescriptionString() const {
const char *s = ""; const char *s = "";
if(h.v == Request::HREQUEST_REFERENCE_XY.v) { if(h == Request::HREQUEST_REFERENCE_XY) {
s = "#XY"; s = "#XY";
} else if(h.v == Request::HREQUEST_REFERENCE_YZ.v) { } else if(h == Request::HREQUEST_REFERENCE_YZ) {
s = "#YZ"; s = "#YZ";
} else if(h.v == Request::HREQUEST_REFERENCE_ZX.v) { } else if(h == Request::HREQUEST_REFERENCE_ZX) {
s = "#ZX"; s = "#ZX";
} else { } else {
switch(type) { switch(type) {
@ -228,10 +228,10 @@ std::string Request::DescriptionString() const {
int Request::IndexOfPoint(hEntity he) const { int Request::IndexOfPoint(hEntity he) const {
if(type == Type::DATUM_POINT) { if(type == Type::DATUM_POINT) {
return (he.v == h.entity(0).v) ? 0 : -1; return (he == h.entity(0)) ? 0 : -1;
} }
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) { for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
if(he.v == h.entity(i + 1).v) { if(he == h.entity(i + 1)) {
return i; return i;
} }
} }

View File

@ -540,7 +540,7 @@ void BitmapFont::AddGlyph(char32_t codepoint, std::shared_ptr<const Pixmap> pixm
BitmapFont::Glyph glyph = {}; BitmapFont::Glyph glyph = {};
glyph.advanceCells = (uint8_t)(pixmap->width / 8); glyph.advanceCells = (uint8_t)(pixmap->width / 8);
glyph.position = nextPosition++; glyph.position = nextPosition++;
glyphs.emplace(codepoint, std::move(glyph)); glyphs.emplace(codepoint, glyph);
for(size_t y = 0; y < pixmap->height; y++) { for(size_t y = 0; y < pixmap->height; y++) {
uint8_t *row = BitmapFontTextureRow(texture, glyph.position, y); uint8_t *row = BitmapFontTextureRow(texture, glyph.position, y);
@ -623,7 +623,8 @@ const BitmapFont::Glyph &BitmapFont::GetGlyph(char32_t codepoint) {
} }
} }
it = glyphs.emplace(codepoint, std::move(glyph)).first; it = glyphs.emplace(codepoint, glyph).first;
textureUpdated = true; textureUpdated = true;
return (*it).second; return (*it).second;
} }
@ -1147,7 +1148,7 @@ PluralExpr::Token PluralExpr::Lex() {
} }
PluralExpr::Token PluralExpr::PopToken() { PluralExpr::Token PluralExpr::PopToken() {
ssassert(stack.size() > 0, "Expected a non-empty stack"); ssassert(!stack.empty(), "Expected a non-empty stack");
Token t = stack.back(); Token t = stack.back();
stack.pop_back(); stack.pop_back();
return t; return t;
@ -1406,7 +1407,7 @@ void GettextParser::Parse() {
} }
} }
if(key.ident == "") { if(key.ident.empty()) {
ssassert(msgstrs.size() == 1, ssassert(msgstrs.size() == 1,
"Expected exactly one header msgstr"); "Expected exactly one header msgstr");
ParseHeader(msgstrs[0]); ParseHeader(msgstrs[0]);

View File

@ -59,6 +59,10 @@ public:
inline hParam param(int i) const; inline hParam param(int i) const;
inline hEquation equation(int i) const; inline hEquation equation(int i) const;
}; };
template<>
struct IsHandleOracle<hGroup> : std::true_type {};
class hRequest { class hRequest {
public: public:
// bits 15: 0 -- request index // bits 15: 0 -- request index
@ -69,6 +73,10 @@ public:
inline bool IsFromReferences() const; inline bool IsFromReferences() const;
}; };
template<>
struct IsHandleOracle<hRequest> : std::true_type {};
class hEntity { class hEntity {
public: public:
// bits 15: 0 -- entity index // bits 15: 0 -- entity index
@ -80,6 +88,10 @@ public:
inline hGroup group() const; inline hGroup group() const;
inline hEquation equation(int i) const; inline hEquation equation(int i) const;
}; };
template<>
struct IsHandleOracle<hEntity> : std::true_type {};
class hParam { class hParam {
public: public:
// bits 15: 0 -- param index // bits 15: 0 -- param index
@ -89,14 +101,24 @@ public:
inline hRequest request() const; inline hRequest request() const;
}; };
template<>
struct IsHandleOracle<hParam> : std::true_type {};
class hStyle { class hStyle {
public: public:
uint32_t v; uint32_t v;
}; };
template<>
struct IsHandleOracle<hStyle> : std::true_type {};
struct EntityId { struct EntityId {
uint32_t v; // entity ID, starting from 0 uint32_t v; // entity ID, starting from 0
}; };
template<>
struct IsHandleOracle<EntityId> : std::true_type {};
struct EntityKey { struct EntityKey {
hEntity input; hEntity input;
int copyNumber; int copyNumber;
@ -111,7 +133,7 @@ struct EntityKeyHash {
}; };
struct EntityKeyEqual { struct EntityKeyEqual {
bool operator()(const EntityKey &a, const EntityKey &b) const { bool operator()(const EntityKey &a, const EntityKey &b) const {
return std::tie(a.input.v, a.copyNumber) == std::tie(b.input.v, b.copyNumber); return std::tie(a.input, a.copyNumber) == std::tie(b.input, b.copyNumber);
} }
}; };
typedef std::unordered_map<EntityKey, EntityId, EntityKeyHash, EntityKeyEqual> EntityMap; typedef std::unordered_map<EntityKey, EntityId, EntityKeyHash, EntityKeyEqual> EntityMap;
@ -129,6 +151,7 @@ public:
N_TRANS, N_TRANS,
N_ROT_AA, N_ROT_AA,
N_ROT_TRANS, N_ROT_TRANS,
N_ROT_AXIS_TRANS,
}; };
enum class Type : uint32_t { enum class Type : uint32_t {
@ -136,6 +159,8 @@ public:
DRAWING_WORKPLANE = 5001, DRAWING_WORKPLANE = 5001,
EXTRUDE = 5100, EXTRUDE = 5100,
LATHE = 5101, LATHE = 5101,
REVOLVE = 5102,
HELIX = 5103,
ROTATE = 5200, ROTATE = 5200,
TRANSLATE = 5201, TRANSLATE = 5201,
LINKED = 5300 LINKED = 5300
@ -258,11 +283,12 @@ public:
hEntity Remap(hEntity in, int copyNumber); hEntity Remap(hEntity in, int copyNumber);
void MakeExtrusionLines(EntityList *el, hEntity in); void MakeExtrusionLines(EntityList *el, hEntity in);
void MakeLatheCircles(IdList<Entity,hEntity> *el, IdList<Param,hParam> *param, hEntity in, Vector pt, Vector axis, int ai); void MakeLatheCircles(IdList<Entity,hEntity> *el, IdList<Param,hParam> *param, hEntity in, Vector pt, Vector axis, int ai);
void MakeLatheSurfacesSelectable(IdList<Entity, hEntity> *el, hEntity in, Vector axis);
void MakeExtrusionTopBottomFaces(EntityList *el, hEntity pt); void MakeExtrusionTopBottomFaces(EntityList *el, hEntity pt);
void CopyEntity(EntityList *el, void CopyEntity(EntityList *el,
Entity *ep, int timesApplied, int remap, Entity *ep, int timesApplied, int remap,
hParam dx, hParam dy, hParam dz, hParam dx, hParam dy, hParam dz,
hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam dist,
CopyAs as); CopyAs as);
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index); void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
@ -363,6 +389,7 @@ public:
POINT_N_ROT_TRANS = 2011, POINT_N_ROT_TRANS = 2011,
POINT_N_COPY = 2012, POINT_N_COPY = 2012,
POINT_N_ROT_AA = 2013, POINT_N_ROT_AA = 2013,
POINT_N_ROT_AXIS_TRANS = 2014,
NORMAL_IN_3D = 3000, NORMAL_IN_3D = 3000,
NORMAL_IN_2D = 3001, NORMAL_IN_2D = 3001,
@ -402,7 +429,7 @@ public:
hEntity distance; hEntity distance;
// The only types that have their own params are points, normals, // The only types that have their own params are points, normals,
// and directions. // and directions.
hParam param[7]; hParam param[8];
// Transformed points/normals/distances have their numerical base // Transformed points/normals/distances have their numerical base
Vector numPoint; Vector numPoint;
@ -593,6 +620,9 @@ public:
inline hParam param(int i) const; inline hParam param(int i) const;
}; };
template<>
struct IsHandleOracle<hConstraint> : std::true_type {};
class ConstraintBase { class ConstraintBase {
public: public:
int tag; int tag;
@ -660,10 +690,10 @@ public:
std::string comment; // since comments are represented as constraints std::string comment; // since comments are represented as constraints
bool Equals(const ConstraintBase &c) const { bool Equals(const ConstraintBase &c) const {
return type == c.type && group.v == c.group.v && workplane.v == c.workplane.v && return type == c.type && group == c.group && workplane == c.workplane &&
valA == c.valA && valP.v == c.valP.v && ptA.v == c.ptA.v && ptB.v == c.ptB.v && valA == c.valA && valP == c.valP && ptA == c.ptA && ptB == c.ptB &&
entityA.v == c.entityA.v && entityB.v == c.entityB.v && entityA == c.entityA && entityB == c.entityB &&
entityC.v == c.entityC.v && entityD.v == c.entityD.v && entityC == c.entityC && entityD == c.entityD &&
other == c.other && other2 == c.other2 && reference == c.reference && other == c.other && other2 == c.other2 && reference == c.reference &&
comment == c.comment; comment == c.comment;
} }
@ -761,6 +791,9 @@ public:
inline hConstraint constraint() const; inline hConstraint constraint() const;
}; };
template<>
struct IsHandleOracle<hEquation> : std::true_type {};
class Equation { class Equation {
public: public:
int tag; int tag;
@ -887,9 +920,9 @@ inline hEquation hGroup::equation(int i) const
{ hEquation r; r.v = (v << 16) | 0x80000000 | (uint32_t)i; return r; } { hEquation r; r.v = (v << 16) | 0x80000000 | (uint32_t)i; return r; }
inline bool hRequest::IsFromReferences() const { inline bool hRequest::IsFromReferences() const {
if(v == Request::HREQUEST_REFERENCE_XY.v) return true; if(*this == Request::HREQUEST_REFERENCE_XY) return true;
if(v == Request::HREQUEST_REFERENCE_YZ.v) return true; if(*this == Request::HREQUEST_REFERENCE_YZ) return true;
if(v == Request::HREQUEST_REFERENCE_ZX.v) return true; if(*this == Request::HREQUEST_REFERENCE_ZX) return true;
return false; return false;
} }
inline hEntity hRequest::entity(int i) const inline hEntity hRequest::entity(int i) const

View File

@ -772,70 +772,48 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
} }
case Command::VOLUME: { case Command::VOLUME: {
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh); Group *g = SK.GetGroup(SS.GW.activeGroup);
double totalVol = g->displayMesh.CalculateVolume();
std::string msg = ssprintf(
_("The volume of the solid model is:\n\n"
" %s"),
SS.MmToStringSI(totalVol, /*dim=*/3).c_str());
double vol = 0; SMesh curMesh = {};
int i; g->thisShell.TriangulateInto(&curMesh);
for(i = 0; i < m->l.n; i++) { double curVol = curMesh.CalculateVolume();
STriangle tr = m->l.elem[i]; if(curVol > 0.0) {
msg += ssprintf(
// Translate to place vertex A at (x, y, 0) _("\nThe volume of current group mesh is:\n\n"
Vector trans = Vector::From(tr.a.x, tr.a.y, 0); " %s"),
tr.a = (tr.a).Minus(trans); SS.MmToStringSI(curVol, /*dim=*/3).c_str());
tr.b = (tr.b).Minus(trans);
tr.c = (tr.c).Minus(trans);
// Rotate to place vertex B on the y-axis. Depending on
// whether the triangle is CW or CCW, C is either to the
// right or to the left of the y-axis. This handles the
// sign of our normal.
Vector u = Vector::From(-tr.b.y, tr.b.x, 0);
u = u.WithMagnitude(1);
Vector v = Vector::From(tr.b.x, tr.b.y, 0);
v = v.WithMagnitude(1);
Vector n = Vector::From(0, 0, 1);
tr.a = (tr.a).DotInToCsys(u, v, n);
tr.b = (tr.b).DotInToCsys(u, v, n);
tr.c = (tr.c).DotInToCsys(u, v, n);
n = tr.Normal().WithMagnitude(1);
// Triangles on edge don't contribute
if(fabs(n.z) < LENGTH_EPS) continue;
// The plane has equation p dot n = a dot n
double d = (tr.a).Dot(n);
// nx*x + ny*y + nz*z = d
// nz*z = d - nx*x - ny*y
double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z;
double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x;
double xc = tr.c.x, yb = tr.b.y;
// I asked Maple for
// int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc);
double integral =
(1.0/3)*(
A*(mbc-mac)+
(1.0/2)*B*(mbc*mbc-mac*mac)
)*(xc*xc*xc)+
(1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+
C*yb*xc+
(1.0/2)*B*yb*yb*xc;
vol += integral;
} }
Message(_("The volume of the solid model is:\n\n"
" %s\n\n" msg += _("\n\nCurved surfaces have been approximated as triangles.\n"
"Curved surfaces have been approximated as triangles.\n" "This introduces error, typically of around 1%.");
"This introduces error, typically of around 1%%."), Message("%s", msg.c_str());
SS.MmToStringSI(vol, /*dim=*/3).c_str());
break; break;
} }
case Command::AREA: { case Command::AREA: {
Group *g = SK.GetGroup(SS.GW.activeGroup); Group *g = SK.GetGroup(SS.GW.activeGroup);
SS.GW.GroupSelection();
auto const &gs = SS.GW.gs;
double scale = SS.MmPerUnit();
if(gs.faces > 0) {
std::vector<uint32_t> faces;
faces.push_back(gs.face[0].v);
if(gs.faces > 1) faces.push_back(gs.face[1].v);
double area = g->displayMesh.CalculateSurfaceArea(faces);
Message(_("The surface area of the selected faces is:\n\n"
" %s\n\n"
"Curves have been approximated as piecewise linear.\n"
"This introduces error, typically of around 1%%."),
SS.MmToStringSI(area, /*dim=*/2).c_str());
break;
}
if(g->polyError.how != PolyError::GOOD) { if(g->polyError.how != PolyError::GOOD) {
Error(_("This group does not contain a correctly-formed " Error(_("This group does not contain a correctly-formed "
"2d closed area. It is open, not coplanar, or self-" "2d closed area. It is open, not coplanar, or self-"
@ -907,7 +885,7 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
int i; int i;
SContour *sc = &(SS.traced.path); SContour *sc = &(SS.traced.path);
for(i = 0; i < sc->l.n; i++) { for(i = 0; i < sc->l.n; i++) {
Vector p = sc->l.elem[i].p; Vector p = sc->l[i].p;
double s = SS.exportScale; double s = SS.exportScale;
fprintf(f, "%.10f, %.10f, %.10f\r\n", fprintf(f, "%.10f, %.10f, %.10f\r\n",
p.x/s, p.y/s, p.z/s); p.x/s, p.y/s, p.z/s);
@ -938,7 +916,7 @@ void SolveSpaceUI::ShowNakedEdges(bool reportOnlyWhenNotOkay) {
root->MakeCertainEdgesInto(&(SS.nakedEdges), root->MakeCertainEdgesInto(&(SS.nakedEdges),
EdgeKind::NAKED_OR_SELF_INTER, /*coplanarIsInter=*/true, &inters, &leaks); EdgeKind::NAKED_OR_SELF_INTER, /*coplanarIsInter=*/true, &inters, &leaks);
if(reportOnlyWhenNotOkay && !inters && !leaks && SS.nakedEdges.l.n == 0) { if(reportOnlyWhenNotOkay && !inters && !leaks && SS.nakedEdges.l.IsEmpty()) {
return; return;
} }
SS.GW.Invalidate(); SS.GW.Invalidate();
@ -954,7 +932,7 @@ void SolveSpaceUI::ShowNakedEdges(bool reportOnlyWhenNotOkay) {
_("\n\nThe model contains %d triangles, from %d surfaces."), _("\n\nThe model contains %d triangles, from %d surfaces."),
g->displayMesh.l.n, g->runningShell.surface.n); g->displayMesh.l.n, g->runningShell.surface.n);
if(SS.nakedEdges.l.n == 0) { if(SS.nakedEdges.l.IsEmpty()) {
Message(_("%s\n\n%s\n\nZero problematic edges, good.%s"), Message(_("%s\n\n%s\n\nZero problematic edges, good.%s"),
intersMsg, leaksMsg, cntMsg.c_str()); intersMsg, leaksMsg, cntMsg.c_str());
} else { } else {
@ -1045,7 +1023,7 @@ BBox Sketch::CalculateEntityBBox(bool includingInvisible) {
// arc center point shouldn't be included in bounding box calculation // arc center point shouldn't be included in bounding box calculation
if(e.IsPoint() && e.h.isFromRequest()) { if(e.IsPoint() && e.h.isFromRequest()) {
Request *r = SK.GetRequest(e.h.request()); Request *r = SK.GetRequest(e.h.request());
if(r->type == Request::Type::ARC_OF_CIRCLE && e.h.v == r->h.entity(1).v) { if(r->type == Request::Type::ARC_OF_CIRCLE && e.h == r->h.entity(1)) {
continue; continue;
} }
} }

View File

@ -165,13 +165,8 @@ enum class Unit : uint32_t {
METERS METERS
}; };
template<class T>
struct CompareHandle {
bool operator()(T lhs, T rhs) const { return lhs.v < rhs.v; }
};
template<class Key, class T> template<class Key, class T>
using handle_map = std::map<Key, T, CompareHandle<Key>>; using handle_map = std::map<Key, T>;
class Group; class Group;
class SSurface; class SSurface;
@ -219,7 +214,7 @@ class ReadUTF8 {
public: public:
ReadUTF8(const std::string &str) : str(str) {} ReadUTF8(const std::string &str) : str(str) {}
utf8_iterator begin() const { return utf8_iterator(&str[0]); } utf8_iterator begin() const { return utf8_iterator(&str[0]); }
utf8_iterator end() const { return utf8_iterator(&str[str.length()]); } utf8_iterator end() const { return utf8_iterator(&str[0] + str.length()); }
}; };
@ -703,6 +698,7 @@ public:
void ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm); void ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm);
void ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename, void ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename,
SMesh *sm, SOutlineList *sol); SMesh *sm, SOutlineList *sol);
void ExportMeshAsVrmlTo(FILE *f, const Platform::Path &filename, SMesh *sm);
void ExportViewOrWireframeTo(const Platform::Path &filename, bool exportWireframe); void ExportViewOrWireframeTo(const Platform::Path &filename, bool exportWireframe);
void ExportSectionTo(const Platform::Path &filename); void ExportSectionTo(const Platform::Path &filename);
void ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl, void ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,

View File

@ -24,17 +24,6 @@ void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) {
// the intersection of srfA and srfB.) Return a new pwl curve with everything // the intersection of srfA and srfB.) Return a new pwl curve with everything
// split. // split.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static Vector LineStart, LineDirection;
static int ByTAlongLine(const void *av, const void *bv)
{
SInter *a = (SInter *)av,
*b = (SInter *)bv;
double ta = (a->p.Minus(LineStart)).DivPivoting(LineDirection),
tb = (b->p.Minus(LineStart)).DivPivoting(LineDirection);
return (ta > tb) ? 1 : -1;
}
SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
SSurface *srfA, SSurface *srfB) const SSurface *srfA, SSurface *srfB) const
{ {
@ -59,7 +48,7 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
agnstB->AllPointsIntersecting(prev.p, p->p, &il, agnstB->AllPointsIntersecting(prev.p, p->p, &il,
/*asSegment=*/true, /*trimmed=*/false, /*inclTangent=*/true); /*asSegment=*/true, /*trimmed=*/false, /*inclTangent=*/true);
if(il.n > 0) { if(!il.IsEmpty()) {
// The intersections were generated by intersecting the pwl // The intersections were generated by intersecting the pwl
// edge against a surface; so they must be refined to lie // edge against a surface; so they must be refined to lie
// exactly on the original curve. // exactly on the original curve.
@ -104,9 +93,14 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
// And now sort them in order along the line. Note that we must // And now sort them in order along the line. Note that we must
// do that after refining, in case the refining would make two // do that after refining, in case the refining would make two
// points switch places. // points switch places.
LineStart = prev.p; const Vector lineStart = prev.p;
LineDirection = (p->p).Minus(prev.p); const Vector lineDirection = (p->p).Minus(prev.p);
qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine); std::sort(il.begin(), il.end(), [&](const SInter &a, const SInter &b) {
double ta = (a.p.Minus(lineStart)).DivProjected(lineDirection);
double tb = (b.p.Minus(lineStart)).DivProjected(lineDirection);
return (ta < tb);
});
// And now uses the intersections to generate our split pwl edge(s) // And now uses the intersections to generate our split pwl edge(s)
Vector prev = Vector::From(VERY_POSITIVE, 0, 0); Vector prev = Vector::From(VERY_POSITIVE, 0, 0);
@ -297,18 +291,18 @@ static void DEBUGEDGELIST(SEdgeList *sel, SSurface *surf) {
void SSurface::FindChainAvoiding(SEdgeList *src, SEdgeList *dest, void SSurface::FindChainAvoiding(SEdgeList *src, SEdgeList *dest,
SPointList *avoid) SPointList *avoid)
{ {
ssassert(src->l.n > 0, "Need at least one edge"); ssassert(!src->l.IsEmpty(), "Need at least one edge");
// Start with an arbitrary edge. // Start with an arbitrary edge.
dest->l.Add(&(src->l.elem[0])); dest->l.Add(src->l.First());
src->l.ClearTags(); src->l.ClearTags();
src->l.elem[0].tag = 1; src->l.First()->tag = 1;
bool added; bool added;
do { do {
added = false; added = false;
// The start and finish of the current edge chain // The start and finish of the current edge chain
Vector s = dest->l.elem[0].a, Vector s = dest->l.First()->a,
f = dest->l.elem[dest->l.n - 1].b; f = dest->l.Last()->b;
// We can attach a new edge at the start or finish, as long as that // We can attach a new edge at the start or finish, as long as that
// start or finish point isn't in the list of points to avoid. // start or finish point isn't in the list of points to avoid.
@ -444,15 +438,15 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
for(sc = into->curve.First(); sc; sc = into->curve.NextAfter(sc)) { for(sc = into->curve.First(); sc; sc = into->curve.NextAfter(sc)) {
if(sc->source != SCurve::Source::INTERSECTION) continue; if(sc->source != SCurve::Source::INTERSECTION) continue;
if(opA) { if(opA) {
if(sc->surfA.v != h.v || sc->surfB.v != ss->h.v) continue; if(sc->surfA != h || sc->surfB != ss->h) continue;
} else { } else {
if(sc->surfB.v != h.v || sc->surfA.v != ss->h.v) continue; if(sc->surfB != h || sc->surfA != ss->h) continue;
} }
int i; int i;
for(i = 1; i < sc->pts.n; i++) { for(i = 1; i < sc->pts.n; i++) {
Vector a = sc->pts.elem[i-1].p, Vector a = sc->pts[i-1].p,
b = sc->pts.elem[i].p; b = sc->pts[i].p;
Point2d auv, buv; Point2d auv, buv;
ss->ClosestPointTo(a, &(auv.x), &(auv.y)); ss->ClosestPointTo(a, &(auv.x), &(auv.y));
@ -512,13 +506,13 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
// our original and intersecting edge lists. // our original and intersecting edge lists.
SEdgeList final = {}; SEdgeList final = {};
while(orig.l.n > 0) { while(!orig.l.IsEmpty()) {
SEdgeList chain = {}; SEdgeList chain = {};
FindChainAvoiding(&orig, &chain, &choosing); FindChainAvoiding(&orig, &chain, &choosing);
// Arbitrarily choose an edge within the chain to classify; they // Arbitrarily choose an edge within the chain to classify; they
// should all be the same, though. // should all be the same, though.
se = &(chain.l.elem[chain.l.n/2]); se = &(chain.l[chain.l.n/2]);
Point2d auv = (se->a).ProjectXy(), Point2d auv = (se->a).ProjectXy(),
buv = (se->b).ProjectXy(); buv = (se->b).ProjectXy();
@ -546,12 +540,12 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
chain.Clear(); chain.Clear();
} }
while(inter.l.n > 0) { while(!inter.l.IsEmpty()) {
SEdgeList chain = {}; SEdgeList chain = {};
FindChainAvoiding(&inter, &chain, &choosing); FindChainAvoiding(&inter, &chain, &choosing);
// Any edge in the chain, same as above. // Any edge in the chain, same as above.
se = &(chain.l.elem[chain.l.n/2]); se = &(chain.l[chain.l.n/2]);
Point2d auv = (se->a).ProjectXy(), Point2d auv = (se->a).ProjectXy(),
buv = (se->b).ProjectXy(); buv = (se->b).ProjectXy();
@ -731,7 +725,7 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type) {
a->MakeClassifyingBsps(this); a->MakeClassifyingBsps(this);
b->MakeClassifyingBsps(this); b->MakeClassifyingBsps(this);
if(b->surface.n == 0 || a->surface.n == 0) { if(b->surface.IsEmpty() || a->surface.IsEmpty()) {
I = 1000000; I = 1000000;
} else { } else {
I = 0; I = 0;
@ -775,19 +769,6 @@ SBspUv *SBspUv::Alloc() {
return (SBspUv *)AllocTemporary(sizeof(SBspUv)); return (SBspUv *)AllocTemporary(sizeof(SBspUv));
} }
static int ByLength(const void *av, const void *bv)
{
SEdge *a = (SEdge *)av,
*b = (SEdge *)bv;
double la = (a->a).Minus(a->b).Magnitude(),
lb = (b->a).Minus(b->b).Magnitude();
// Sort in descending order, longest first. This improves numerical
// stability for the normals.
return (la < lb) ? 1 : -1;
}
SBspUv *SBspUv::From(SEdgeList *el, SSurface *srf) { SBspUv *SBspUv::From(SEdgeList *el, SSurface *srf) {
SEdgeList work = {}; SEdgeList work = {};
@ -795,8 +776,12 @@ SBspUv *SBspUv::From(SEdgeList *el, SSurface *srf) {
for(se = el->l.First(); se; se = el->l.NextAfter(se)) { for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
work.AddEdge(se->a, se->b, se->auxA, se->auxB); work.AddEdge(se->a, se->b, se->auxA, se->auxB);
} }
qsort(work.l.elem, work.l.n, sizeof(work.l.elem[0]), ByLength); std::sort(work.l.begin(), work.l.end(), [](SEdge const &a, SEdge const &b) {
double la = (a.a).Minus(a.b).Magnitude(), lb = (b.a).Minus(b.b).Magnitude();
// Sort in descending order, longest first. This improves numerical
// stability for the normals.
return la > lb;
});
SBspUv *bsp = NULL; SBspUv *bsp = NULL;
for(se = work.l.First(); se; se = work.l.NextAfter(se)) { for(se = work.l.First(); se; se = work.l.NextAfter(se)) {
bsp = InsertOrCreateEdge(bsp, (se->a).ProjectXy(), (se->b).ProjectXy(), srf); bsp = InsertOrCreateEdge(bsp, (se->a).ProjectXy(), (se->b).ProjectXy(), srf);

View File

@ -239,12 +239,12 @@ void SBezierList::CullIdenticalBeziers(bool both) {
l.ClearTags(); l.ClearTags();
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
SBezier *bi = &(l.elem[i]), bir; SBezier *bi = &(l[i]), bir;
bir = *bi; bir = *bi;
bir.Reverse(); bir.Reverse();
for(j = i + 1; j < l.n; j++) { for(j = i + 1; j < l.n; j++) {
SBezier *bj = &(l.elem[j]); SBezier *bj = &(l[j]);
if(bj->Equals(bi) || if(bj->Equals(bi) ||
bj->Equals(&bir)) bj->Equals(&bir))
{ {
@ -311,8 +311,8 @@ bool SBezierList::GetPlaneContainingBeziers(Vector *p, Vector *u, Vector *v,
int i; int i;
// Get any point on any Bezier; or an arbitrary point if list is empty. // Get any point on any Bezier; or an arbitrary point if list is empty.
if(l.n > 0) { if(!l.IsEmpty()) {
pt = l.elem[0].Start(); pt = l[0].Start();
} else { } else {
pt = Vector::From(0, 0, 0); pt = Vector::From(0, 0, 0);
} }
@ -393,7 +393,7 @@ SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
if(sbl->l.n < 1) return loop; if(sbl->l.n < 1) return loop;
sbl->l.ClearTags(); sbl->l.ClearTags();
SBezier *first = &(sbl->l.elem[0]); SBezier *first = &(sbl->l[0]);
first->tag = 1; first->tag = 1;
loop.l.Add(first); loop.l.Add(first);
Vector start = first->Start(); Vector start = first->Start();
@ -402,11 +402,11 @@ SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
sbl->l.RemoveTagged(); sbl->l.RemoveTagged();
while(sbl->l.n > 0 && !hanging.Equals(start)) { while(!sbl->l.IsEmpty() && !hanging.Equals(start)) {
int i; int i;
bool foundNext = false; bool foundNext = false;
for(i = 0; i < sbl->l.n; i++) { for(i = 0; i < sbl->l.n; i++) {
SBezier *test = &(sbl->l.elem[i]); SBezier *test = &(sbl->l[i]);
if((test->Finish()).Equals(hanging) && test->auxA == auxA) { if((test->Finish()).Equals(hanging) && test->auxA == auxA) {
test->Reverse(); test->Reverse();
@ -470,15 +470,15 @@ void SBezierLoop::MakePwlInto(SContour *sc, double chordTol) const {
} }
} }
// Ensure that it's exactly closed, not just within a numerical tolerance. // Ensure that it's exactly closed, not just within a numerical tolerance.
if((sc->l.elem[sc->l.n - 1].p).Equals(sc->l.elem[0].p)) { if((sc->l.Last()->p).Equals(sc->l.First()->p)) {
sc->l.elem[sc->l.n - 1] = sc->l.elem[0]; *sc->l.Last() = *sc->l.First();
} }
} }
bool SBezierLoop::IsClosed() const { bool SBezierLoop::IsClosed() const {
if(l.n < 1) return false; if(l.IsEmpty()) return false;
Vector s = l.elem[0].Start(), Vector s = l.First()->Start(),
f = l.elem[l.n-1].Finish(); f = l.Last()->Finish();
return s.Equals(f); return s.Equals(f);
} }
@ -497,7 +497,7 @@ SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
SBezierLoopSet ret = {}; SBezierLoopSet ret = {};
*allClosed = true; *allClosed = true;
while(sbl->l.n > 0) { while(!sbl->l.IsEmpty()) {
bool thisClosed; bool thisClosed;
SBezierLoop loop; SBezierLoop loop;
loop = SBezierLoop::FromCurves(sbl, &thisClosed, errorAt); loop = SBezierLoop::FromCurves(sbl, &thisClosed, errorAt);
@ -512,7 +512,7 @@ SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
} else { } else {
ret.l.Add(&loop); ret.l.Add(&loop);
poly->AddEmptyContour(); poly->AddEmptyContour();
loop.MakePwlInto(&(poly->l.elem[poly->l.n-1]), chordTol); loop.MakePwlInto(poly->l.Last(), chordTol);
} }
} }
@ -553,14 +553,14 @@ double SBezierLoopSet::SignedArea() {
void SBezierLoopSet::MakePwlInto(SPolygon *sp) const { void SBezierLoopSet::MakePwlInto(SPolygon *sp) const {
for(const SBezierLoop *sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) { for(const SBezierLoop *sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) {
sp->AddEmptyContour(); sp->AddEmptyContour();
sbl->MakePwlInto(&(sp->l.elem[sp->l.n - 1])); sbl->MakePwlInto(sp->l.Last());
} }
} }
void SBezierLoopSet::Clear() { void SBezierLoopSet::Clear() {
int i; int i;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
(l.elem[i]).Clear(); (l[i]).Clear();
} }
l.Clear(); l.Clear();
} }
@ -618,7 +618,7 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
for(pt = sc->l.First(); pt; pt = sc->l.NextAfter(pt)) { for(pt = sc->l.First(); pt; pt = sc->l.NextAfter(pt)) {
double u, v; double u, v;
srfuv->ClosestPointTo(pt->p, &u, &v); srfuv->ClosestPointTo(pt->p, &u, &v);
spuv.l.elem[spuv.l.n - 1].AddPoint(Vector::From(u, v, 0)); spuv.l.Last()->AddPoint(Vector::From(u, v, 0));
} }
} }
spuv.normal = Vector::From(0, 0, 1); // must be, since it's in xy plane now spuv.normal = Vector::From(0, 0, 1); // must be, since it's in xy plane now
@ -630,8 +630,8 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
// works for curved surfaces too (important for STEP export). // works for curved surfaces too (important for STEP export).
spuv.FixContourDirections(); spuv.FixContourDirections();
for(i = 0; i < spuv.l.n; i++) { for(i = 0; i < spuv.l.n; i++) {
SContour *contour = &(spuv.l.elem[i]); SContour *contour = &(spuv.l[i]);
SBezierLoop *bl = &(sbls.l.elem[i]); SBezierLoop *bl = &(sbls.l[i]);
if(contour->tag) { if(contour->tag) {
// This contour got reversed in the polygon to make the directions // This contour got reversed in the polygon to make the directions
// consistent, so the same must be necessary for the Bezier loop. // consistent, so the same must be necessary for the Bezier loop.
@ -648,7 +648,7 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
while(loopsRemaining) { while(loopsRemaining) {
loopsRemaining = false; loopsRemaining = false;
for(i = 0; i < sbls.l.n; i++) { for(i = 0; i < sbls.l.n; i++) {
SBezierLoop *loop = &(sbls.l.elem[i]); SBezierLoop *loop = &(sbls.l[i]);
if(loop->tag != OUTER_LOOP) continue; if(loop->tag != OUTER_LOOP) continue;
// Check if this contour contains any outer loops; if it does, then // Check if this contour contains any outer loops; if it does, then
@ -656,12 +656,12 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
// will steal their holes, since their holes also lie inside this // will steal their holes, since their holes also lie inside this
// contour. // contour.
for(j = 0; j < sbls.l.n; j++) { for(j = 0; j < sbls.l.n; j++) {
SBezierLoop *outer = &(sbls.l.elem[j]); SBezierLoop *outer = &(sbls.l[j]);
if(i == j) continue; if(i == j) continue;
if(outer->tag != OUTER_LOOP) continue; if(outer->tag != OUTER_LOOP) continue;
Vector p = spuv.l.elem[j].AnyEdgeMidpoint(); Vector p = spuv.l[j].AnyEdgeMidpoint();
if(spuv.l.elem[i].ContainsPointProjdToNormal(spuv.normal, p)) { if(spuv.l[i].ContainsPointProjdToNormal(spuv.normal, p)) {
break; break;
} }
} }
@ -675,16 +675,16 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
loop->tag = USED_LOOP; loop->tag = USED_LOOP;
outerAndInners.l.Add(loop); outerAndInners.l.Add(loop);
int auxA = 0; int auxA = 0;
if(loop->l.n > 0) auxA = loop->l.elem[0].auxA; if(loop->l.n > 0) auxA = loop->l[0].auxA;
for(j = 0; j < sbls.l.n; j++) { for(j = 0; j < sbls.l.n; j++) {
SBezierLoop *inner = &(sbls.l.elem[j]); SBezierLoop *inner = &(sbls.l[j]);
if(inner->tag != INNER_LOOP) continue; if(inner->tag != INNER_LOOP) continue;
if(inner->l.n < 1) continue; if(inner->l.n < 1) continue;
if(inner->l.elem[0].auxA != auxA) continue; if(inner->l[0].auxA != auxA) continue;
Vector p = spuv.l.elem[j].AnyEdgeMidpoint(); Vector p = spuv.l[j].AnyEdgeMidpoint();
if(spuv.l.elem[i].ContainsPointProjdToNormal(spuv.normal, p)) { if(spuv.l[i].ContainsPointProjdToNormal(spuv.normal, p)) {
outerAndInners.l.Add(inner); outerAndInners.l.Add(inner);
inner->tag = USED_LOOP; inner->tag = USED_LOOP;
} }
@ -702,7 +702,7 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
// to screw up on that stuff. So just add them onto the open curve list. // to screw up on that stuff. So just add them onto the open curve list.
// Very ugly, but better than losing curves. // Very ugly, but better than losing curves.
for(i = 0; i < sbls.l.n; i++) { for(i = 0; i < sbls.l.n; i++) {
SBezierLoop *loop = &(sbls.l.elem[i]); SBezierLoop *loop = &(sbls.l[i]);
if(loop->tag == USED_LOOP) continue; if(loop->tag == USED_LOOP) continue;
if(openContours) { if(openContours) {
@ -804,11 +804,11 @@ void SCurve::RemoveShortSegments(SSurface *srfA, SSurface *srfB) {
if(pts.n <= 3) return; if(pts.n <= 3) return;
pts.ClearTags(); pts.ClearTags();
Vector prev = pts.elem[0].p; Vector prev = pts[0].p;
int i, a; int i, a;
for(i = 1; i < pts.n - 1; i++) { for(i = 1; i < pts.n - 1; i++) {
SCurvePt *sct = &(pts.elem[i]), SCurvePt *sct = &(pts[i]),
*scn = &(pts.elem[i+1]); *scn = &(pts[i+1]);
if(sct->vertex) { if(sct->vertex) {
prev = sct->p; prev = sct->p;
continue; continue;
@ -849,12 +849,12 @@ STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) {
SCurve *sc = shell->curve.FindById(hsc); SCurve *sc = shell->curve.FindById(hsc);
if(backwards) { if(backwards) {
stb.finish = sc->pts.elem[0].p; stb.finish = sc->pts[0].p;
stb.start = sc->pts.elem[sc->pts.n - 1].p; stb.start = sc->pts.Last()->p;
stb.backwards = true; stb.backwards = true;
} else { } else {
stb.start = sc->pts.elem[0].p; stb.start = sc->pts[0].p;
stb.finish = sc->pts.elem[sc->pts.n - 1].p; stb.finish = sc->pts.Last()->p;
stb.backwards = false; stb.backwards = false;
} }

View File

@ -13,11 +13,12 @@ void SShell::MergeCoincidentSurfaces() {
SSurface *si, *sj; SSurface *si, *sj;
for(i = 0; i < surface.n; i++) { for(i = 0; i < surface.n; i++) {
si = &(surface.elem[i]); si = &(surface[i]);
if(si->tag) continue; if(si->tag) continue;
// Let someone else clean up the empty surfaces; we can certainly merge // Let someone else clean up the empty surfaces; we can certainly merge
// them, but we don't know how to calculate a reasonable bounding box. // them, but we don't know how to calculate a reasonable bounding box.
if(si->trim.n == 0) continue; if(si->trim.IsEmpty())
continue;
// And for now we handle only coincident planes, so no sense wasting // And for now we handle only coincident planes, so no sense wasting
// time on other surfaces. // time on other surfaces.
if(si->degm != 1 || si->degn != 1) continue; if(si->degm != 1 || si->degn != 1) continue;
@ -30,7 +31,7 @@ void SShell::MergeCoincidentSurfaces() {
mergedThisTime = false; mergedThisTime = false;
for(j = i + 1; j < surface.n; j++) { for(j = i + 1; j < surface.n; j++) {
sj = &(surface.elem[j]); sj = &(surface[j]);
if(sj->tag) continue; if(sj->tag) continue;
if(!sj->CoincidentWith(si, /*sameNormal=*/true)) continue; if(!sj->CoincidentWith(si, /*sameNormal=*/true)) continue;
if(!sj->color.Equals(si->color)) continue; if(!sj->color.Equals(si->color)) continue;
@ -59,8 +60,8 @@ void SShell::MergeCoincidentSurfaces() {
// new srf // new srf
SCurve *sc; SCurve *sc;
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
if(sc->surfA.v == sj->h.v) sc->surfA = si->h; if(sc->surfA == sj->h) sc->surfA = si->h;
if(sc->surfB.v == sj->h.v) sc->surfB = si->h; if(sc->surfB == sj->h) sc->surfB = si->h;
} }
} }

View File

@ -155,7 +155,7 @@ void SBezier::ClosestPointTo(Vector p, double *t, bool mustConverge) const {
Vector dp = TangentAt(*t); Vector dp = TangentAt(*t);
Vector pc = p.ClosestPointOnLine(p0, dp); Vector pc = p.ClosestPointOnLine(p0, dp);
*t += (pc.Minus(p0)).DivPivoting(dp); *t += (pc.Minus(p0)).DivProjected(dp);
} }
if(mustConverge) { if(mustConverge) {
dbp("didn't converge (closest point on bezier curve)"); dbp("didn't converge (closest point on bezier curve)");
@ -231,7 +231,7 @@ void SBezier::MakePwlInto(SEdgeList *sel, double chordTol) const {
MakePwlInto(&lv, chordTol); MakePwlInto(&lv, chordTol);
int i; int i;
for(i = 1; i < lv.n; i++) { for(i = 1; i < lv.n; i++) {
sel->AddEdge(lv.elem[i-1], lv.elem[i]); sel->AddEdge(lv[i-1], lv[i]);
} }
lv.Clear(); lv.Clear();
} }
@ -242,7 +242,7 @@ void SBezier::MakePwlInto(List<SCurvePt> *l, double chordTol) const {
for(i = 0; i < lv.n; i++) { for(i = 0; i < lv.n; i++) {
SCurvePt scpt; SCurvePt scpt;
scpt.tag = 0; scpt.tag = 0;
scpt.p = lv.elem[i]; scpt.p = lv[i];
scpt.vertex = (i == 0) || (i == (lv.n - 1)); scpt.vertex = (i == 0) || (i == (lv.n - 1));
l->Add(&scpt); l->Add(&scpt);
} }
@ -253,7 +253,7 @@ void SBezier::MakePwlInto(SContour *sc, double chordTol) const {
MakePwlInto(&lv, chordTol); MakePwlInto(&lv, chordTol);
int i; int i;
for(i = 0; i < lv.n; i++) { for(i = 0; i < lv.n; i++) {
sc->AddPoint(lv.elem[i]); sc->AddPoint(lv[i]);
} }
lv.Clear(); lv.Clear();
} }

View File

@ -311,7 +311,7 @@ void SSurface::AllPointsIntersecting(Vector a, Vector b,
} }
int i; int i;
for(i = 0; i < ip_n; i++) { for(i = 0; i < ip_n; i++) {
double t = (ip[i].Minus(ap)).DivPivoting(bp.Minus(ap)); double t = (ip[i].Minus(ap)).DivProjected(bp.Minus(ap));
// This is a point on the circle; but is it on the arc? // This is a point on the circle; but is it on the arc?
Point2d pp = ap.Plus((bp.Minus(ap)).ScaledBy(t)); Point2d pp = ap.Plus((bp.Minus(ap)).ScaledBy(t));
double theta = atan2(pp.y, pp.x); double theta = atan2(pp.y, pp.x);
@ -342,19 +342,19 @@ void SSurface::AllPointsIntersecting(Vector a, Vector b,
int i, j; int i, j;
for(i = 0; i < inters.n; i++) { for(i = 0; i < inters.n; i++) {
for(j = i + 1; j < inters.n; j++) { for(j = i + 1; j < inters.n; j++) {
if(inters.elem[i].p.Equals(inters.elem[j].p)) { if(inters[i].p.Equals(inters[j].p)) {
inters.elem[j].tag = 1; inters[j].tag = 1;
} }
} }
} }
inters.RemoveTagged(); inters.RemoveTagged();
for(i = 0; i < inters.n; i++) { for(i = 0; i < inters.n; i++) {
Point2d puv = inters.elem[i].p; Point2d puv = inters[i].p;
// Make sure the point lies within the finite line segment // Make sure the point lies within the finite line segment
Vector pxyz = PointAt(puv.x, puv.y); Vector pxyz = PointAt(puv.x, puv.y);
double t = (pxyz.Minus(a)).DivPivoting(ba); double t = (pxyz.Minus(a)).DivProjected(ba);
if(asSegment && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) { if(asSegment && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) {
continue; continue;
} }
@ -463,7 +463,7 @@ bool SShell::ClassifyEdge(Class *indir, Class *outdir,
} }
if(edge_inters == 2) { if(edge_inters == 2) {
// TODO, make this use the appropriate curved normals //! @todo make this use the appropriate curved normals
double dotp[2]; double dotp[2];
for(int i = 0; i < 2; i++) { for(int i = 0; i < 2; i++) {
dotp[i] = edge_n_out.DirectionCosineWith(inter_surf_n[i]); dotp[i] = edge_n_out.DirectionCosineWith(inter_surf_n[i]);
@ -566,7 +566,7 @@ bool SShell::ClassifyEdge(Class *indir, Class *outdir,
SInter *si; SInter *si;
for(si = l.First(); si; si = l.NextAfter(si)) { for(si = l.First(); si; si = l.NextAfter(si)) {
double t = ((si->p).Minus(p)).DivPivoting(ray); double t = ((si->p).Minus(p)).DivProjected(ray);
if(t*ray.Magnitude() < -LENGTH_EPS) { if(t*ray.Magnitude() < -LENGTH_EPS) {
// wrong side, doesn't count // wrong side, doesn't count
continue; continue;

View File

@ -63,18 +63,19 @@ bool SSurface::IsCylinder(Vector *axis, Vector *center, double *r,
return true; return true;
} }
SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, // Create a surface patch by revolving and possibly translating a curve.
double thetas, double thetaf) // Works for sections up to but not including 180 degrees.
{ SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, double thetas,
double thetaf, double dists,
double distf) { // s is start, f is finish
SSurface ret = {}; SSurface ret = {};
ret.degm = sb->deg; ret.degm = sb->deg;
ret.degn = 2; ret.degn = 2;
double dtheta = fabs(WRAP_SYMMETRIC(thetaf - thetas, 2*PI)); double dtheta = fabs(WRAP_SYMMETRIC(thetaf - thetas, 2*PI));
double w = cos(dtheta / 2);
// We now wish to revolve the curve about the z axis // Revolve the curve about the z axis
int i; int i;
for(i = 0; i <= ret.degm; i++) { for(i = 0; i <= ret.degm; i++) {
Vector p = sb->ctrl[i]; Vector p = sb->ctrl[i];
@ -82,32 +83,26 @@ SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
Vector ps = p.RotatedAbout(pt, axis, thetas), Vector ps = p.RotatedAbout(pt, axis, thetas),
pf = p.RotatedAbout(pt, axis, thetaf); pf = p.RotatedAbout(pt, axis, thetaf);
Vector ct; // The middle control point should be at the intersection of the tangents at ps and pf.
// This is equivalent but works for 0 <= angle < 180 degrees.
Vector mid = ps.Plus(pf).ScaledBy(0.5);
Vector c = ps.ClosestPointOnLine(pt, axis);
Vector ct = mid.Minus(c).ScaledBy(1 / (w * w)).Plus(c);
// not sure this is needed
if(ps.Equals(pf)) { if(ps.Equals(pf)) {
// Degenerate case: a control point lies on the axis of revolution, ps = c;
// so we get three coincident control points. ct = c;
ct = ps; pf = c;
} else {
// Normal case, the control point sweeps out a circle.
Vector c = ps.ClosestPointOnLine(pt, axis);
Vector rs = ps.Minus(c),
rf = pf.Minus(c);
Vector ts = axis.Cross(rs),
tf = axis.Cross(rf);
ct = Vector::AtIntersectionOfLines(ps, ps.Plus(ts),
pf, pf.Plus(tf),
NULL, NULL, NULL);
} }
// moving along the axis can create hilical surfaces (or straight extrusion if
ret.ctrl[i][0] = ps; // thetas==thetaf)
ret.ctrl[i][1] = ct; ret.ctrl[i][0] = ps.Plus(axis.ScaledBy(dists));
ret.ctrl[i][2] = pf; ret.ctrl[i][1] = ct.Plus(axis.ScaledBy((dists + distf) / 2));
ret.ctrl[i][2] = pf.Plus(axis.ScaledBy(distf));
ret.weight[i][0] = sb->weight[i]; ret.weight[i][0] = sb->weight[i];
ret.weight[i][1] = sb->weight[i]*cos(dtheta/2); ret.weight[i][1] = sb->weight[i] * w;
ret.weight[i][2] = sb->weight[i]; ret.weight[i][2] = sb->weight[i];
} }
@ -240,7 +235,7 @@ void SSurface::MakeTrimEdgesInto(SEdgeList *sel, MakeAs flags,
increment = 1; increment = 1;
} }
for(i = first; i != (last + increment); i += increment) { for(i = first; i != (last + increment); i += increment) {
Vector tpt, *pt = &(sc->pts.elem[i].p); Vector tpt, *pt = &(sc->pts[i].p);
if(flags == MakeAs::UV) { if(flags == MakeAs::UV) {
ClosestPointTo(*pt, &u, &v); ClosestPointTo(*pt, &u, &v);
@ -348,11 +343,11 @@ void SSurface::MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *
int sp, fp; int sp, fp;
for(sp = 0; sp < sc->pts.n; sp++) { for(sp = 0; sp < sc->pts.n; sp++) {
if(s.Equals(sc->pts.elem[sp].p)) break; if(s.Equals(sc->pts[sp].p)) break;
} }
if(sp >= sc->pts.n) return; if(sp >= sc->pts.n) return;
for(fp = sp; fp < sc->pts.n; fp++) { for(fp = sp; fp < sc->pts.n; fp++) {
if(f.Equals(sc->pts.elem[fp].p)) break; if(f.Equals(sc->pts[fp].p)) break;
} }
if(fp >= sc->pts.n) return; if(fp >= sc->pts.n) return;
// So now the curve we want goes from elem[sp] to elem[fp] // So now the curve we want goes from elem[sp] to elem[fp]
@ -365,8 +360,8 @@ void SSurface::MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *
for(;;) { for(;;) {
// So construct a cubic Bezier with the correct endpoints // So construct a cubic Bezier with the correct endpoints
// and tangents for the current span. // and tangents for the current span.
Vector st = sc->pts.elem[sp].p, Vector st = sc->pts[sp].p,
ft = sc->pts.elem[fpt].p, ft = sc->pts[fpt].p,
sf = ft.Minus(st); sf = ft.Minus(st);
double m = sf.Magnitude() / 3; double m = sf.Magnitude() / 3;
@ -383,7 +378,7 @@ void SSurface::MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *
int i; int i;
bool tooFar = false; bool tooFar = false;
for(i = sp + 1; i <= (fpt - 1); i++) { for(i = sp + 1; i <= (fpt - 1); i++) {
Vector p = sc->pts.elem[i].p; Vector p = sc->pts[i].p;
double t; double t;
sb.ClosestPointTo(p, &t, /*mustConverge=*/false); sb.ClosestPointTo(p, &t, /*mustConverge=*/false);
Vector pp = sb.PointAt(t); Vector pp = sb.PointAt(t);
@ -440,7 +435,7 @@ void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
STriMeta meta = { face, color }; STriMeta meta = { face, color };
for(i = start; i < sm->l.n; i++) { for(i = start; i < sm->l.n; i++) {
STriangle *st = &(sm->l.elem[i]); STriangle *st = &(sm->l[i]);
st->meta = meta; st->meta = meta;
st->an = NormalAt(st->a.x, st->a.y); st->an = NormalAt(st->a.x, st->a.y);
st->bn = NormalAt(st->b.x, st->b.y); st->bn = NormalAt(st->b.x, st->b.y);
@ -593,10 +588,10 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, Rgb
int i; int i;
for(i = 0; i < trimLines.n; i++) { for(i = 0; i < trimLines.n; i++) {
TrimLine *tl = &(trimLines.elem[i]); TrimLine *tl = &(trimLines[i]);
SSurface *ss = surface.FindById(tl->hs); SSurface *ss = surface.FindById(tl->hs);
TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]); TrimLine *tlp = &(trimLines[WRAP(i-1, trimLines.n)]);
STrimBy stb; STrimBy stb;
stb = STrimBy::EntireCurve(this, tl->hc, /*backwards=*/true); stb = STrimBy::EntireCurve(this, tl->hc, /*backwards=*/true);
@ -611,20 +606,11 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, Rgb
} }
} }
bool SShell::CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis, double da, double dx)
typedef struct { // Check that the direction of revolution/extrusion ends up parallel to the normal of
hSSurface d[4]; // the sketch, on the side of the axis where the sketch is.
} Revolved;
void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color, Group *group)
{ {
SBezierLoop *sbl; SBezierLoop *sbl;
int i0 = surface.n, i;
// Normalize the axis direction so that the direction of revolution
// ends up parallel to the normal of the sketch, on the side of the
// axis where the sketch is.
Vector pto; Vector pto;
double md = VERY_NEGATIVE; double md = VERY_NEGATIVE;
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) { for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
@ -634,80 +620,151 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
// if we choose a point that lies on the axis, for example. // if we choose a point that lies on the axis, for example.
// (And our surface will be self-intersecting if the sketch // (And our surface will be self-intersecting if the sketch
// spans the axis, so don't worry about that.) // spans the axis, so don't worry about that.)
for(i = 0; i <= sb->deg; i++) { for(int i = 0; i <= sb->deg; i++) {
Vector p = sb->ctrl[i]; Vector p = sb->ctrl[i];
double d = p.DistanceToLine(pt, axis); double d = p.DistanceToLine(pt, axis);
if(d > md) { if(d > md) {
md = d; md = d;
pto = p; pto = p;
} }
} }
} }
} }
Vector ptc = pto.ClosestPointOnLine(pt, axis), Vector ptc = pto.ClosestPointOnLine(pt, axis),
up = (pto.Minus(ptc)).WithMagnitude(1), up = axis.Cross(pto.Minus(ptc)).ScaledBy(da),
vp = (sbls->normal).Cross(up); vp = up.Plus(axis.ScaledBy(dx));
if(vp.Dot(axis) < 0) {
axis = axis.ScaledBy(-1); return (vp.Dot(sbls->normal) > 0);
}
// sketch must not contain the axis of revolution as a non-construction line for helix
void SShell::MakeFromHelicalRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
RgbaColor color, Group *group, double angles,
double anglef, double dists, double distf) {
int i0 = surface.n; // number of pre-existing surfaces
SBezierLoop *sbl;
// for testing - hard code the axial distance, and number of sections.
// distance will need to be parameters in the future.
double dist = distf - dists;
int sections = fabs(anglef - angles) / (PI / 2) + 1;
double wedge = (anglef - angles) / sections;
if(CheckNormalAxisRelationship(sbls, pt, axis, anglef-angles, distf-dists)) {
swap(angles, anglef);
swap(dists, distf);
dist = -dist;
wedge = -wedge;
} }
// Now we actually build and trim the surfaces. // Define a coordinate system to contain the original sketch, and get
// a bounding box in that csys
Vector n = sbls->normal.ScaledBy(-1);
Vector u = n.Normal(0), v = n.Normal(1);
Vector orig = sbls->point;
double umax = 1e-10, umin = 1e10;
sbls->GetBoundingProjd(u, orig, &umin, &umax);
double vmax = 1e-10, vmin = 1e10;
sbls->GetBoundingProjd(v, orig, &vmin, &vmax);
// and now fix things up so that all u and v lie between 0 and 1
orig = orig.Plus(u.ScaledBy(umin));
orig = orig.Plus(v.ScaledBy(vmin));
u = u.ScaledBy(umax - umin);
v = v.ScaledBy(vmax - vmin);
// So we can now generate the end caps of the extrusion within
// a translated and rotated (and maybe mirrored) version of that csys.
SSurface s0, s1;
s0 = SSurface::FromPlane(orig.RotatedAbout(pt, axis, angles).Plus(axis.ScaledBy(dists)),
u.RotatedAbout(axis, angles), v.RotatedAbout(axis, angles));
s0.color = color;
s1 = SSurface::FromPlane(
orig.Plus(u).RotatedAbout(pt, axis, anglef).Plus(axis.ScaledBy(distf)),
u.ScaledBy(-1).RotatedAbout(axis, anglef), v.RotatedAbout(axis, anglef));
s1.color = color;
hSSurface hs0 = surface.AddAndAssignId(&s0), hs1 = surface.AddAndAssignId(&s1);
// Now we actually build and trim the swept surfaces. One loop at a time.
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) { for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
int i, j; int i, j;
SBezier *sb; SBezier *sb;
List<Revolved> hsl = {}; List<std::vector<hSSurface>> hsl = {};
// This is where all the NURBS are created and Remapped to the generating curve
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) { for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
Revolved revs; std::vector<hSSurface> revs(sections);
for(j = 0; j < 4; j++) { for(j = 0; j < sections; j++) {
if(sb->deg == 1 && if((dist == 0) && sb->deg == 1 &&
(sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS && (sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS &&
(sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS) (sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS) {
{
// This is a line on the axis of revolution; it does // This is a line on the axis of revolution; it does
// not contribute a surface. // not contribute a surface.
revs.d[j].v = 0; revs[j].v = 0;
} else { } else {
SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis, SSurface ss = SSurface::FromRevolutionOf(
(PI/2)*j, sb, pt, axis, angles + (wedge)*j, angles + (wedge) * (j + 1),
(PI/2)*(j+1)); dists + j * dist / sections, dists + (j + 1) * dist / sections);
ss.color = color; ss.color = color;
if(sb->entity != 0) { if(sb->entity != 0) {
hEntity he; hEntity he;
he.v = sb->entity; he.v = sb->entity;
hEntity hface = group->Remap(he, Group::REMAP_LINE_TO_FACE); hEntity hface = group->Remap(he, Group::REMAP_LINE_TO_FACE);
if(SK.entity.FindByIdNoOops(hface) != NULL) { if(SK.entity.FindByIdNoOops(hface) != NULL) {
ss.face = hface.v; ss.face = hface.v;
} }
} }
revs.d[j] = surface.AddAndAssignId(&ss); revs[j] = surface.AddAndAssignId(&ss);
} }
} }
hsl.Add(&revs); hsl.Add(&revs);
} }
// Still the same loop. Need to create trim curves
for(i = 0; i < sbl->l.n; i++) { for(i = 0; i < sbl->l.n; i++) {
Revolved revs = hsl.elem[i], std::vector<hSSurface> revs = hsl[i], revsp = hsl[WRAP(i - 1, sbl->l.n)];
revsp = hsl.elem[WRAP(i-1, sbl->l.n)];
sb = &(sbl->l.elem[i]); sb = &(sbl->l[i]);
for(j = 0; j < 4; j++) { // we generate one more curve than we did surfaces
for(j = 0; j <= sections; j++) {
SCurve sc; SCurve sc;
Quaternion qs = Quaternion::From(axis, (PI/2)*j); Quaternion qs = Quaternion::From(axis, angles + wedge * j);
// we want Q*(x - p) + p = Q*x + (p - Q*p) // we want Q*(x - p) + p = Q*x + (p - Q*p)
Vector ts = pt.Minus(qs.Rotate(pt)); Vector ts =
pt.Minus(qs.Rotate(pt)).Plus(axis.ScaledBy(dists + j * dist / sections));
// If this input curve generate a surface, then trim that // If this input curve generated a surface, then trim that
// surface with the rotated version of the input curve. // surface with the rotated version of the input curve.
if(revs.d[j].v) { if(revs[0].v) { // not d[j] because crash on j==sections
sc = {}; sc = {};
sc.isExact = true; sc.isExact = true;
sc.exact = sb->TransformedBy(ts, qs, 1.0); sc.exact = sb->TransformedBy(ts, qs, 1.0);
(sc.exact).MakePwlInto(&(sc.pts)); (sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs.d[j];
sc.surfB = revs.d[WRAP(j-1, 4)];
// the surfaces already exist so trim with this curve
if(j < sections) {
sc.surfA = revs[j];
} else {
sc.surfA = hs1; // end cap
}
if(j > 0) {
sc.surfB = revs[j - 1];
} else {
sc.surfB = hs0; // staring cap
}
hSCurve hcb = curve.AddAndAssignId(&sc);
STrimBy stb;
stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/true);
(surface.FindById(sc.surfA))->trim.Add(&stb);
stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/false);
(surface.FindById(sc.surfB))->trim.Add(&stb);
} else if(j == 0) { // curve was on the rotation axis and is shared by the end caps.
sc = {};
sc.isExact = true;
sc.exact = sb->TransformedBy(ts, qs, 1.0);
(sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = hs1; // end cap
sc.surfB = hs0; // staring cap
hSCurve hcb = curve.AddAndAssignId(&sc); hSCurve hcb = curve.AddAndAssignId(&sc);
STrimBy stb; STrimBy stb;
@ -719,19 +776,17 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
// And if this input curve and the one after it both generated // And if this input curve and the one after it both generated
// surfaces, then trim both of those by the appropriate // surfaces, then trim both of those by the appropriate
// circle. // curve based on the control points.
if(revs.d[j].v && revsp.d[j].v) { if((j < sections) && revs[j].v && revsp[j].v) {
SSurface *ss = surface.FindById(revs.d[j]); SSurface *ss = surface.FindById(revs[j]);
sc = {}; sc = {};
sc.isExact = true; sc.isExact = true;
sc.exact = SBezier::From(ss->ctrl[0][0], sc.exact = SBezier::From(ss->ctrl[0][0], ss->ctrl[0][1], ss->ctrl[0][2]);
ss->ctrl[0][1],
ss->ctrl[0][2]);
sc.exact.weight[1] = ss->weight[0][1]; sc.exact.weight[1] = ss->weight[0][1];
(sc.exact).MakePwlInto(&(sc.pts)); (sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs.d[j]; sc.surfA = revs[j];
sc.surfB = revsp.d[j]; sc.surfB = revsp[j];
hSCurve hcc = curve.AddAndAssignId(&sc); hSCurve hcc = curve.AddAndAssignId(&sc);
@ -747,8 +802,123 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
hsl.Clear(); hsl.Clear();
} }
if(dist == 0) {
MakeFirstOrderRevolvedSurfaces(pt, axis, i0);
}
}
void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color,
Group *group) {
int i0 = surface.n; // number of pre-existing surfaces
SBezierLoop *sbl;
if(CheckNormalAxisRelationship(sbls, pt, axis, 1.0, 0.0)) {
axis = axis.ScaledBy(-1);
}
// Now we actually build and trim the surfaces.
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
int i, j;
SBezier *sb;
List<std::vector<hSSurface>> hsl = {};
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
std::vector<hSSurface> revs(4);
for(j = 0; j < 4; j++) {
if(sb->deg == 1 &&
(sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS &&
(sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS)
{
// This is a line on the axis of revolution; it does
// not contribute a surface.
revs[j].v = 0;
} else {
SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis, (PI / 2) * j,
(PI / 2) * (j + 1), 0.0, 0.0);
ss.color = color;
if(sb->entity != 0) {
hEntity he;
he.v = sb->entity;
hEntity hface = group->Remap(he, Group::REMAP_LINE_TO_FACE);
if(SK.entity.FindByIdNoOops(hface) != NULL) {
ss.face = hface.v;
}
}
revs[j] = surface.AddAndAssignId(&ss);
}
}
hsl.Add(&revs);
}
for(i = 0; i < sbl->l.n; i++) {
std::vector<hSSurface> revs = hsl[i],
revsp = hsl[WRAP(i-1, sbl->l.n)];
sb = &(sbl->l[i]);
for(j = 0; j < 4; j++) {
SCurve sc;
Quaternion qs = Quaternion::From(axis, (PI/2)*j);
// we want Q*(x - p) + p = Q*x + (p - Q*p)
Vector ts = pt.Minus(qs.Rotate(pt));
// If this input curve generate a surface, then trim that
// surface with the rotated version of the input curve.
if(revs[j].v) {
sc = {};
sc.isExact = true;
sc.exact = sb->TransformedBy(ts, qs, 1.0);
(sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs[j];
sc.surfB = revs[WRAP(j-1, 4)];
hSCurve hcb = curve.AddAndAssignId(&sc);
STrimBy stb;
stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/true);
(surface.FindById(sc.surfA))->trim.Add(&stb);
stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/false);
(surface.FindById(sc.surfB))->trim.Add(&stb);
}
// And if this input curve and the one after it both generated
// surfaces, then trim both of those by the appropriate
// circle.
if(revs[j].v && revsp[j].v) {
SSurface *ss = surface.FindById(revs[j]);
sc = {};
sc.isExact = true;
sc.exact = SBezier::From(ss->ctrl[0][0],
ss->ctrl[0][1],
ss->ctrl[0][2]);
sc.exact.weight[1] = ss->weight[0][1];
(sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs[j];
sc.surfB = revsp[j];
hSCurve hcc = curve.AddAndAssignId(&sc);
STrimBy stb;
stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/false);
(surface.FindById(sc.surfA))->trim.Add(&stb);
stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/true);
(surface.FindById(sc.surfB))->trim.Add(&stb);
}
}
}
hsl.Clear();
}
MakeFirstOrderRevolvedSurfaces(pt, axis, i0);
}
void SShell::MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0) {
int i;
for(i = i0; i < surface.n; i++) { for(i = i0; i < surface.n; i++) {
SSurface *srf = &(surface.elem[i]); SSurface *srf = &(surface[i]);
// Revolution of a line; this is potentially a plane, which we can // Revolution of a line; this is potentially a plane, which we can
// rewrite to have degree (1, 1). // rewrite to have degree (1, 1).
@ -823,9 +993,7 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
continue; continue;
} }
} }
} }
} }
void SShell::MakeFromCopyOf(SShell *a) { void SShell::MakeFromCopyOf(SShell *a) {
@ -880,7 +1048,7 @@ void SShell::TriangulateInto(SMesh *sm) {
} }
bool SShell::IsEmpty() const { bool SShell::IsEmpty() const {
return (surface.n == 0); return surface.IsEmpty();
} }
void SShell::Clear() { void SShell::Clear() {
@ -896,4 +1064,3 @@ void SShell::Clear() {
} }
curve.Clear(); curve.Clear();
} }

View File

@ -63,11 +63,17 @@ public:
uint32_t v; uint32_t v;
}; };
template<>
struct IsHandleOracle<hSSurface> : std::true_type {};
class hSCurve { class hSCurve {
public: public:
uint32_t v; uint32_t v;
}; };
template<>
struct IsHandleOracle<hSCurve> : std::true_type {};
// Stuff for rational polynomial curves, of degree one to three. These are // Stuff for rational polynomial curves, of degree one to three. These are
// our inputs, and are also calculated for certain exact surface-surface // our inputs, and are also calculated for certain exact surface-surface
// intersections. // intersections.
@ -282,8 +288,8 @@ public:
Point2d cached; Point2d cached;
static SSurface FromExtrusionOf(SBezier *spc, Vector t0, Vector t1); static SSurface FromExtrusionOf(SBezier *spc, Vector t0, Vector t1);
static SSurface FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, static SSurface FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, double thetas,
double thetas, double thetaf); double thetaf, double dists, double distf);
static SSurface FromPlane(Vector pt, Vector u, Vector v); static SSurface FromPlane(Vector pt, Vector u, Vector v);
static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q, static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q,
double scale, double scale,
@ -376,9 +382,12 @@ public:
void MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, void MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1,
RgbaColor color); RgbaColor color);
bool CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis, double da, double dx);
void MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, void MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
RgbaColor color, Group *group); RgbaColor color, Group *group);
void MakeFromHelicalRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color,
Group *group, double angles, double anglef, double dists, double distf);
void MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0);
void MakeFromUnionOf(SShell *a, SShell *b); void MakeFromUnionOf(SShell *a, SShell *b);
void MakeFromDifferenceOf(SShell *a, SShell *b); void MakeFromDifferenceOf(SShell *a, SShell *b);
void MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type); void MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type);

View File

@ -277,7 +277,7 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
int i; int i;
for(i = 0; i < lv.n - 1; i++) { for(i = 0; i < lv.n - 1; i++) {
Vector pa = lv.elem[i], pb = lv.elem[i+1]; Vector pa = lv[i], pb = lv[i+1];
pa = pa.Minus(axis.ScaledBy(pa.Dot(axis))); pa = pa.Minus(axis.ScaledBy(pa.Dot(axis)));
pb = pb.Minus(axis.ScaledBy(pb.Dot(axis))); pb = pb.Minus(axis.ScaledBy(pb.Dot(axis)));
pa = pa.Plus(axisc); pa = pa.Plus(axisc);
@ -326,12 +326,13 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
srfB->AllPointsIntersecting(se->a, se->b, &lsi, srfB->AllPointsIntersecting(se->a, se->b, &lsi,
/*asSegment=*/true, /*trimmed=*/true, /*inclTangent=*/false); /*asSegment=*/true, /*trimmed=*/true, /*inclTangent=*/false);
if(lsi.n == 0) continue; if(lsi.IsEmpty())
continue;
// Find the other surface that this curve trims. // Find the other surface that this curve trims.
hSCurve hsc = { (uint32_t)se->auxA }; hSCurve hsc = { (uint32_t)se->auxA };
SCurve *sc = shA->curve.FindById(hsc); SCurve *sc = shA->curve.FindById(hsc);
hSSurface hother = (sc->surfA.v == srfA->h.v) ? hSSurface hother = (sc->surfA == srfA->h) ?
sc->surfB : sc->surfA; sc->surfB : sc->surfA;
SSurface *other = shA->surface.FindById(hother); SSurface *other = shA->surface.FindById(hother);
@ -368,10 +369,10 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
sc.isExact = false; sc.isExact = false;
sc.source = SCurve::Source::INTERSECTION; sc.source = SCurve::Source::INTERSECTION;
Vector start = spl.l.elem[0].p, Vector start = spl.l[0].p,
startv = spl.l.elem[0].auxv; startv = spl.l[0].auxv;
spl.l.ClearTags(); spl.l.ClearTags();
spl.l.elem[0].tag = 1; spl.l[0].tag = 1;
spl.l.RemoveTagged(); spl.l.RemoveTagged();
// Our chord tolerance is whatever the user specified // Our chord tolerance is whatever the user specified

View File

@ -36,7 +36,7 @@ void SPolygon::UvTriangulateInto(SMesh *m, SSurface *srf) {
SContour merged = {}; SContour merged = {};
top->tag = 1; top->tag = 1;
top->CopyInto(&merged); top->CopyInto(&merged);
(merged.l.n)--; merged.l.RemoveLast(1);
// List all of the edges, for testing whether bridges work. // List all of the edges, for testing whether bridges work.
SEdgeList el = {}; SEdgeList el = {};
@ -114,7 +114,7 @@ bool SContour::BridgeToContour(SContour *sc,
// point. // point.
int sco = 0; int sco = 0;
for(i = 0; i < (sc->l.n - 1); i++) { for(i = 0; i < (sc->l.n - 1); i++) {
if((sc->l.elem[i].p).EqualsExactly(sc->xminPt)) { if((sc->l[i].p).EqualsExactly(sc->xminPt)) {
sco = i; sco = i;
} }
} }
@ -124,7 +124,7 @@ bool SContour::BridgeToContour(SContour *sc,
int thiso = 0; int thiso = 0;
double dmin = 1e10; double dmin = 1e10;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
Vector p = l.elem[i].p; Vector p = l[i].p;
double d = (p.Minus(sc->xminPt)).MagSquared(); double d = (p.Minus(sc->xminPt)).MagSquared();
if(d < dmin) { if(d < dmin) {
dmin = d; dmin = d;
@ -140,7 +140,7 @@ bool SContour::BridgeToContour(SContour *sc,
// merge them there, without a bridge. // merge them there, without a bridge.
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
thisp = WRAP(i+thiso, l.n); thisp = WRAP(i+thiso, l.n);
a = l.elem[thisp].p; a = l[thisp].p;
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) { for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
if(f->Equals(a)) break; if(f->Equals(a)) break;
@ -149,7 +149,7 @@ bool SContour::BridgeToContour(SContour *sc,
for(j = 0; j < (sc->l.n - 1); j++) { for(j = 0; j < (sc->l.n - 1); j++) {
scp = WRAP(j+sco, (sc->l.n - 1)); scp = WRAP(j+sco, (sc->l.n - 1));
b = sc->l.elem[scp].p; b = sc->l[scp].p;
if(a.Equals(b)) { if(a.Equals(b)) {
goto haveEdge; goto haveEdge;
@ -160,7 +160,7 @@ bool SContour::BridgeToContour(SContour *sc,
// If that fails, look for a bridge that does not intersect any edges. // If that fails, look for a bridge that does not intersect any edges.
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
thisp = WRAP(i+thiso, l.n); thisp = WRAP(i+thiso, l.n);
a = l.elem[thisp].p; a = l[thisp].p;
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) { for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
if(f->Equals(a)) break; if(f->Equals(a)) break;
@ -169,7 +169,7 @@ bool SContour::BridgeToContour(SContour *sc,
for(j = 0; j < (sc->l.n - 1); j++) { for(j = 0; j < (sc->l.n - 1); j++) {
scp = WRAP(j+sco, (sc->l.n - 1)); scp = WRAP(j+sco, (sc->l.n - 1));
b = sc->l.elem[scp].p; b = sc->l[scp].p;
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) { for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
if(f->Equals(b)) break; if(f->Equals(b)) break;
@ -190,15 +190,15 @@ bool SContour::BridgeToContour(SContour *sc,
haveEdge: haveEdge:
SContour merged = {}; SContour merged = {};
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
merged.AddPoint(l.elem[i].p); merged.AddPoint(l[i].p);
if(i == thisp) { if(i == thisp) {
// less than or equal; need to duplicate the join point // less than or equal; need to duplicate the join point
for(j = 0; j <= (sc->l.n - 1); j++) { for(j = 0; j <= (sc->l.n - 1); j++) {
int jp = WRAP(j + scp, (sc->l.n - 1)); int jp = WRAP(j + scp, (sc->l.n - 1));
merged.AddPoint((sc->l.elem[jp]).p); merged.AddPoint((sc->l[jp]).p);
} }
// and likewise duplicate join point for the outer curve // and likewise duplicate join point for the outer curve
merged.AddPoint(l.elem[i].p); merged.AddPoint(l[i].p);
} }
} }
@ -218,9 +218,9 @@ bool SContour::IsEar(int bp, double scaledEps) const {
cp = WRAP(bp+1, l.n); cp = WRAP(bp+1, l.n);
STriangle tr = {}; STriangle tr = {};
tr.a = l.elem[ap].p; tr.a = l[ap].p;
tr.b = l.elem[bp].p; tr.b = l[bp].p;
tr.c = l.elem[cp].p; tr.c = l[cp].p;
if((tr.a).Equals(tr.c)) { if((tr.a).Equals(tr.c)) {
// This is two coincident and anti-parallel edges. Zero-area, so // This is two coincident and anti-parallel edges. Zero-area, so
@ -244,7 +244,7 @@ bool SContour::IsEar(int bp, double scaledEps) const {
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
if(i == ap || i == bp || i == cp) continue; if(i == ap || i == bp || i == cp) continue;
Vector p = l.elem[i].p; Vector p = l[i].p;
if(p.OutsideAndNotOn(maxv, minv)) continue; if(p.OutsideAndNotOn(maxv, minv)) continue;
// A point on the edge of the triangle is considered to be inside, // A point on the edge of the triangle is considered to be inside,
@ -266,9 +266,9 @@ void SContour::ClipEarInto(SMesh *m, int bp, double scaledEps) {
cp = WRAP(bp+1, l.n); cp = WRAP(bp+1, l.n);
STriangle tr = {}; STriangle tr = {};
tr.a = l.elem[ap].p; tr.a = l[ap].p;
tr.b = l.elem[bp].p; tr.b = l[bp].p;
tr.c = l.elem[cp].p; tr.c = l[cp].p;
if(tr.Normal().MagSquared() < scaledEps*scaledEps) { if(tr.Normal().MagSquared() < scaledEps*scaledEps) {
// A vertex with more than two edges will cause us to generate // A vertex with more than two edges will cause us to generate
// zero-area triangles, which must be culled. // zero-area triangles, which must be culled.
@ -278,11 +278,11 @@ void SContour::ClipEarInto(SMesh *m, int bp, double scaledEps) {
// By deleting the point at bp, we may change the ear-ness of the points // By deleting the point at bp, we may change the ear-ness of the points
// on either side. // on either side.
l.elem[ap].ear = EarType::UNKNOWN; l[ap].ear = EarType::UNKNOWN;
l.elem[cp].ear = EarType::UNKNOWN; l[cp].ear = EarType::UNKNOWN;
l.ClearTags(); l.ClearTags();
l.elem[bp].tag = 1; l[bp].tag = 1;
l.RemoveTagged(); l.RemoveTagged();
} }
@ -299,23 +299,23 @@ void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) {
// Clean the original contour by removing any zero-length edges. // Clean the original contour by removing any zero-length edges.
l.ClearTags(); l.ClearTags();
for(i = 1; i < l.n; i++) { for(i = 1; i < l.n; i++) {
if((l.elem[i].p).Equals(l.elem[i-1].p)) { if((l[i].p).Equals(l[i-1].p)) {
l.elem[i].tag = 1; l[i].tag = 1;
} }
} }
l.RemoveTagged(); l.RemoveTagged();
// Now calculate the ear-ness of each vertex // Now calculate the ear-ness of each vertex
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
(l.elem[i]).ear = IsEar(i, scaledEps) ? EarType::EAR : EarType::NOT_EAR; (l[i]).ear = IsEar(i, scaledEps) ? EarType::EAR : EarType::NOT_EAR;
} }
bool toggle = false; bool toggle = false;
while(l.n > 3) { while(l.n > 3) {
// Some points may have changed ear-ness, so recalculate // Some points may have changed ear-ness, so recalculate
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
if(l.elem[i].ear == EarType::UNKNOWN) { if(l[i].ear == EarType::UNKNOWN) {
(l.elem[i]).ear = IsEar(i, scaledEps) ? (l[i]).ear = IsEar(i, scaledEps) ?
EarType::EAR : EarType::NOT_EAR; EarType::EAR : EarType::NOT_EAR;
} }
} }
@ -328,7 +328,7 @@ void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) {
int offset = toggle ? -1 : 0; int offset = toggle ? -1 : 0;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
int ear = WRAP(i+offset, l.n); int ear = WRAP(i+offset, l.n);
if(l.elem[ear].ear == EarType::EAR) { if(l[ear].ear == EarType::EAR) {
if(srf->degm == 1 && srf->degn == 1) { if(srf->degm == 1 && srf->degn == 1) {
// This is a plane; any ear is a good ear. // This is a plane; any ear is a good ear.
bestEar = ear; bestEar = ear;
@ -337,8 +337,8 @@ void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) {
// If we are triangulating a curved surface, then try to // If we are triangulating a curved surface, then try to
// clip ears that have a small chord tolerance from the // clip ears that have a small chord tolerance from the
// surface. // surface.
Vector prev = l.elem[WRAP((i+offset-1), l.n)].p, Vector prev = l[WRAP((i+offset-1), l.n)].p,
next = l.elem[WRAP((i+offset+1), l.n)].p; next = l[WRAP((i+offset+1), l.n)].p;
double tol = srf->ChordToleranceForEdge(prev, next); double tol = srf->ChordToleranceForEdge(prev, next);
if(tol < bestChordTol - scaledEps) { if(tol < bestChordTol - scaledEps) {
bestEar = ear; bestEar = ear;
@ -444,8 +444,8 @@ void SPolygon::UvGridTriangulateInto(SMesh *mesh, SSurface *srf) {
int i, j; int i, j;
for(i = 0; i < (li.n - 1); i++) { for(i = 0; i < (li.n - 1); i++) {
for(j = 0; j < (lj.n - 1); j++) { for(j = 0; j < (lj.n - 1); j++) {
double us = li.elem[i], uf = li.elem[i+1], double us = li[i], uf = li[i+1],
vs = lj.elem[j], vf = lj.elem[j+1]; vs = lj[j], vf = lj[j+1];
Vector a = Vector::From(us, vs, 0), Vector a = Vector::From(us, vs, 0),
b = Vector::From(us, vf, 0), b = Vector::From(us, vf, 0),

View File

@ -59,7 +59,7 @@ void Style::CreateDefaultStyle(hStyle h) {
bool isDefaultStyle = true; bool isDefaultStyle = true;
const Default *d; const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) { for(d = &(Defaults[0]); d->h.v; d++) {
if(d->h.v == h.v) break; if(d->h == h) break;
} }
if(!d->h.v) { if(!d->h.v) {
// Not a default style; so just create it the same as our default // Not a default style; so just create it the same as our default
@ -337,7 +337,7 @@ hStyle Style::ForEntity(hEntity he) {
// Otherwise, we use the default rules. // Otherwise, we use the default rules.
hStyle hs; hStyle hs;
if(e->group.v != SS.GW.activeGroup.v) { if(e->group != SS.GW.activeGroup) {
hs.v = INACTIVE_GRP; hs.v = INACTIVE_GRP;
} else if(e->construction) { } else if(e->construction) {
hs.v = CONSTRUCTION; hs.v = CONSTRUCTION;

View File

@ -20,28 +20,29 @@ const double System::RANK_MAG_TOLERANCE = 1e-4;
const double System::CONVERGE_TOLERANCE = (LENGTH_EPS/(1e2)); const double System::CONVERGE_TOLERANCE = (LENGTH_EPS/(1e2));
bool System::WriteJacobian(int tag) { bool System::WriteJacobian(int tag) {
int a, i, j;
j = 0; int j = 0;
for(a = 0; a < param.n; a++) { for(auto &p : param) {
if(j >= MAX_UNKNOWNS) return false; if(j >= MAX_UNKNOWNS)
return false;
Param *p = &(param.elem[a]); if(p.tag != tag)
if(p->tag != tag) continue; continue;
mat.param[j] = p->h; mat.param[j] = p.h;
j++; j++;
} }
mat.n = j; mat.n = j;
i = 0; int i = 0;
for(a = 0; a < eq.n; a++) {
for(auto &e : eq) {
if(i >= MAX_UNKNOWNS) return false; if(i >= MAX_UNKNOWNS) return false;
Equation *e = &(eq.elem[a]); if(e.tag != tag)
if(e->tag != tag) continue; continue;
mat.eq[i] = e->h; mat.eq[i] = e.h;
Expr *f = e->e->DeepCopyWithParamsAsPointers(&param, &(SK.param)); Expr *f = e.e->DeepCopyWithParamsAsPointers(&param, &(SK.param));
f = f->FoldConstants(); f = f->FoldConstants();
// Hash table (61 bits) to accelerate generation of zero partials. // Hash table (61 bits) to accelerate generation of zero partials.
@ -79,16 +80,14 @@ void System::EvalJacobian() {
bool System::IsDragged(hParam p) { bool System::IsDragged(hParam p) {
hParam *pp; hParam *pp;
for(pp = dragged.First(); pp; pp = dragged.NextAfter(pp)) { for(pp = dragged.First(); pp; pp = dragged.NextAfter(pp)) {
if(p.v == pp->v) return true; if(p == *pp) return true;
} }
return false; return false;
} }
void System::SolveBySubstitution() { void System::SolveBySubstitution() {
int i; for(auto &teq : eq) {
for(i = 0; i < eq.n; i++) { Expr *tex = teq.e;
Equation *teq = &(eq.elem[i]);
Expr *tex = teq->e;
if(tex->op == Expr::Op::MINUS && if(tex->op == Expr::Op::MINUS &&
tex->a->op == Expr::Op::PARAM && tex->a->op == Expr::Op::PARAM &&
@ -108,22 +107,19 @@ void System::SolveBySubstitution() {
std::swap(a, b); std::swap(a, b);
} }
int j; for(auto &req : eq) {
for(j = 0; j < eq.n; j++) { req.e->Substitute(a, b); // A becomes B, B unchanged
Equation *req = &(eq.elem[j]);
(req->e)->Substitute(a, b); // A becomes B, B unchanged
} }
for(j = 0; j < param.n; j++) { for(auto &rp : param) {
Param *rp = &(param.elem[j]); if(rp.substd == a) {
if(rp->substd.v == a.v) { rp.substd = b;
rp->substd = b;
} }
} }
Param *ptr = param.FindById(a); Param *ptr = param.FindById(a);
ptr->tag = VAR_SUBSTITUTED; ptr->tag = VAR_SUBSTITUTED;
ptr->substd = b; ptr->substd = b;
teq->tag = EQ_SUBSTITUTED; teq.tag = EQ_SUBSTITUTED;
} }
} }
} }
@ -324,12 +320,11 @@ bool System::NewtonSolve(int tag) {
} }
void System::WriteEquationsExceptFor(hConstraint hc, Group *g) { void System::WriteEquationsExceptFor(hConstraint hc, Group *g) {
int i;
// Generate all the equations from constraints in this group // Generate all the equations from constraints in this group
for(i = 0; i < SK.constraint.n; i++) { for(auto &con : SK.constraint) {
ConstraintBase *c = &(SK.constraint.elem[i]); ConstraintBase *c = &con;
if(c->group.v != g->h.v) continue; if(c->group != g->h) continue;
if(c->h.v == hc.v) continue; if(c->h == hc) continue;
if(c->HasLabel() && c->type != Constraint::Type::COMMENT && if(c->HasLabel() && c->type != Constraint::Type::COMMENT &&
g->allDimsReference) g->allDimsReference)
@ -349,9 +344,9 @@ void System::WriteEquationsExceptFor(hConstraint hc, Group *g) {
c->GenerateEquations(&eq); c->GenerateEquations(&eq);
} }
// And the equations from entities // And the equations from entities
for(i = 0; i < SK.entity.n; i++) { for(auto &ent : SK.entity) {
EntityBase *e = &(SK.entity.elem[i]); EntityBase *e = &ent;
if(e->group.v != g->h.v) continue; if(e->group != g->h) continue;
e->GenerateEquations(&eq); e->GenerateEquations(&eq);
} }
@ -360,12 +355,12 @@ void System::WriteEquationsExceptFor(hConstraint hc, Group *g) {
} }
void System::FindWhichToRemoveToFixJacobian(Group *g, List<hConstraint> *bad, bool forceDofCheck) { void System::FindWhichToRemoveToFixJacobian(Group *g, List<hConstraint> *bad, bool forceDofCheck) {
int a, i; int a;
for(a = 0; a < 2; a++) { for(a = 0; a < 2; a++) {
for(i = 0; i < SK.constraint.n; i++) { for(auto &con : SK.constraint) {
ConstraintBase *c = &(SK.constraint.elem[i]); ConstraintBase *c = &con;
if(c->group.v != g->h.v) continue; if(c->group != g->h) continue;
if((c->type == Constraint::Type::POINTS_COINCIDENT && a == 0) || if((c->type == Constraint::Type::POINTS_COINCIDENT && a == 0) ||
(c->type != Constraint::Type::POINTS_COINCIDENT && a == 1)) (c->type != Constraint::Type::POINTS_COINCIDENT && a == 1))
{ {
@ -409,11 +404,11 @@ SolveResult System::Solve(Group *g, int *rank, int *dof, List<hConstraint> *bad,
/* /*
dbp("%d equations", eq.n); dbp("%d equations", eq.n);
for(i = 0; i < eq.n; i++) { for(i = 0; i < eq.n; i++) {
dbp(" %.3f = %s = 0", eq.elem[i].e->Eval(), eq.elem[i].e->Print()); dbp(" %.3f = %s = 0", eq[i].e->Eval(), eq[i].e->Print());
} }
dbp("%d parameters", param.n); dbp("%d parameters", param.n);
for(i = 0; i < param.n; i++) { for(i = 0; i < param.n; i++) {
dbp(" param %08x at %.3f", param.elem[i].h.v, param.elem[i].val); dbp(" param %08x at %.3f", param[i].h.v, param[i].val);
} */ } */
// All params and equations are assigned to group zero. // All params and equations are assigned to group zero.
@ -431,18 +426,18 @@ SolveResult System::Solve(Group *g, int *rank, int *dof, List<hConstraint> *bad,
// the system is consistent yet, but if it isn't then we'll catch that // the system is consistent yet, but if it isn't then we'll catch that
// later. // later.
int alone = 1; int alone = 1;
for(i = 0; i < eq.n; i++) { for(auto &e : eq) {
Equation *e = &(eq.elem[i]); if(e.tag != 0)
if(e->tag != 0) continue; continue;
hParam hp = e->e->ReferencedParams(&param); hParam hp = e.e->ReferencedParams(&param);
if(hp.v == Expr::NO_PARAMS.v) continue; if(hp == Expr::NO_PARAMS) continue;
if(hp.v == Expr::MULTIPLE_PARAMS.v) continue; if(hp == Expr::MULTIPLE_PARAMS) continue;
Param *p = param.FindById(hp); Param *p = param.FindById(hp);
if(p->tag != 0) continue; // let rank test catch inconsistency if(p->tag != 0) continue; // let rank test catch inconsistency
e->tag = alone; e.tag = alone;
p->tag = alone; p->tag = alone;
WriteJacobian(alone); WriteJacobian(alone);
if(!NewtonSolve(alone)) { if(!NewtonSolve(alone)) {
@ -480,23 +475,23 @@ SolveResult System::Solve(Group *g, int *rank, int *dof, List<hConstraint> *bad,
} }
// System solved correctly, so write the new values back in to the // System solved correctly, so write the new values back in to the
// main parameter table. // main parameter table.
for(i = 0; i < param.n; i++) { for(auto &p : param) {
Param *p = &(param.elem[i]);
double val; double val;
if(p->tag == VAR_SUBSTITUTED) { if(p.tag == VAR_SUBSTITUTED) {
val = param.FindById(p->substd)->val; val = param.FindById(p.substd)->val;
} else { } else {
val = p->val; val = p.val;
} }
Param *pp = SK.GetParam(p->h); Param *pp = SK.GetParam(p.h);
pp->val = val; pp->val = val;
pp->known = true; pp->known = true;
pp->free = p->free; pp->free = p.free;
} }
return rankOk ? SolveResult::OKAY : SolveResult::REDUNDANT_OKAY; return rankOk ? SolveResult::OKAY : SolveResult::REDUNDANT_OKAY;
didnt_converge: didnt_converge:
SK.constraint.ClearTags(); SK.constraint.ClearTags();
// Not using range-for here because index is used in additional ways
for(i = 0; i < eq.n; i++) { for(i = 0; i < eq.n; i++) {
if(ffabs(mat.B.num[i]) > CONVERGE_TOLERANCE || isnan(mat.B.num[i])) { if(ffabs(mat.B.num[i]) > CONVERGE_TOLERANCE || isnan(mat.B.num[i])) {
// This constraint is unsatisfied. // This constraint is unsatisfied.
@ -553,20 +548,19 @@ void System::MarkParamsFree(bool find) {
// If requested, find all the free (unbound) variables. This might be // If requested, find all the free (unbound) variables. This might be
// more than the number of degrees of freedom. Don't always do this, // more than the number of degrees of freedom. Don't always do this,
// because the display would get annoying and it's slow. // because the display would get annoying and it's slow.
for(int i = 0; i < param.n; i++) { for(auto &p : param) {
Param *p = &(param.elem[i]); p.free = false;
p->free = false;
if(find) { if(find) {
if(p->tag == 0) { if(p.tag == 0) {
p->tag = VAR_DOF_TEST; p.tag = VAR_DOF_TEST;
WriteJacobian(0); WriteJacobian(0);
EvalJacobian(); EvalJacobian();
int rank = CalculateRank(); int rank = CalculateRank();
if(rank == mat.m) { if(rank == mat.m) {
p->free = true; p.free = true;
} }
p->tag = 0; p.tag = 0;
} }
} }
} }

View File

@ -57,8 +57,9 @@ void TextWindow::ScreenToggleGroupShown(int link, uint32_t v) {
} }
void TextWindow::ScreenShowGroupsSpecial(int link, uint32_t v) { void TextWindow::ScreenShowGroupsSpecial(int link, uint32_t v) {
bool state = link == 's'; bool state = link == 's';
for(int i = 0; i < SK.groupOrder.n; i++) { for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]); Group *g = SK.GetGroup(hg);
g->visible = state; g->visible = state;
} }
SS.GW.persistentDirty = true; SS.GW.persistentDirty = true;
@ -71,7 +72,7 @@ void TextWindow::ScreenActivateGroup(int link, uint32_t v) {
void TextWindow::ReportHowGroupSolved(hGroup hg) { void TextWindow::ReportHowGroupSolved(hGroup hg) {
SS.GW.ClearSuper(); SS.GW.ClearSuper();
SS.TW.GoToScreen(Screen::GROUP_SOLVE_INFO); SS.TW.GoToScreen(Screen::GROUP_SOLVE_INFO);
SS.TW.shown.group.v = hg.v; SS.TW.shown.group = hg;
SS.ScheduleShowTW(); SS.ScheduleShowTW();
} }
void TextWindow::ScreenHowGroupSolved(int link, uint32_t v) { void TextWindow::ScreenHowGroupSolved(int link, uint32_t v) {
@ -98,12 +99,13 @@ void TextWindow::ShowListOfGroups() {
Printf(true, "%Ft active"); Printf(true, "%Ft active");
Printf(false, "%Ft shown dof group-name%E"); Printf(false, "%Ft shown dof group-name%E");
int i;
bool afterActive = false; bool afterActive = false;
for(i = 0; i < SK.groupOrder.n; i++) { bool backgroundParity = false;
Group *g = SK.GetGroup(SK.groupOrder.elem[i]); for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(hg);
std::string s = g->DescriptionString(); std::string s = g->DescriptionString();
bool active = (g->h.v == SS.GW.activeGroup.v); bool active = (g->h == SS.GW.activeGroup);
bool shown = g->visible; bool shown = g->visible;
bool ok = g->IsSolvedOkay(); bool ok = g->IsSolvedOkay();
bool warn = (g->type == Group::Type::DRAWING_WORKPLANE && bool warn = (g->type == Group::Type::DRAWING_WORKPLANE &&
@ -120,31 +122,33 @@ void TextWindow::ShowListOfGroups() {
sprintf(sdof, "%-3d", dof); sprintf(sdof, "%-3d", dof);
} }
} }
bool ref = (g->h.v == Group::HGROUP_REFERENCES.v); bool ref = (g->h == Group::HGROUP_REFERENCES);
Printf(false, "%Bp%Fd " Printf(false,
"%Bp%Fd "
"%Ft%s%Fb%D%f%Ll%s%E " "%Ft%s%Fb%D%f%Ll%s%E "
"%Fb%s%D%f%Ll%s%E " "%Fb%s%D%f%Ll%s%E "
"%Fp%D%f%s%Ll%s%E " "%Fp%D%f%s%Ll%s%E "
"%Fl%Ll%D%f%s", "%Fl%Ll%D%f%s",
// Alternate between light and dark backgrounds, for readability // Alternate between light and dark backgrounds, for readability
(i & 1) ? 'd' : 'a', backgroundParity ? 'd' : 'a',
// Link that activates the group // Link that activates the group
ref ? " " : "", ref ? " " : "",
g->h.v, (&TextWindow::ScreenActivateGroup), g->h.v, (&TextWindow::ScreenActivateGroup),
ref ? "" : (active ? radioTrue : radioFalse), ref ? "" : (active ? radioTrue : radioFalse),
// Link that hides or shows the group // Link that hides or shows the group
afterActive ? " - " : "", afterActive ? " - " : "",
g->h.v, (&TextWindow::ScreenToggleGroupShown), g->h.v, (&TextWindow::ScreenToggleGroupShown),
afterActive ? "" : (shown ? checkTrue : checkFalse), afterActive ? "" : (shown ? checkTrue : checkFalse),
// Link to the errors, if a problem occurred while solving // Link to the errors, if a problem occurred while solving
ok ? (warn ? 'm' : (dof > 0 ? 'i' : 's')) : 'x', ok ? (warn ? 'm' : (dof > 0 ? 'i' : 's')) : 'x',
g->h.v, (&TextWindow::ScreenHowGroupSolved), g->h.v, (&TextWindow::ScreenHowGroupSolved),
ok ? (warn ? "err" : sdof) : "", ok ? (warn ? "err" : sdof) : "",
ok ? "" : "ERR", ok ? "" : "ERR",
// Link to a screen that gives more details on the group // Link to a screen that gives more details on the group
g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str()); g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str());
if(active) afterActive = true; if(active) afterActive = true;
backgroundParity = !backgroundParity;
} }
Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E", Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E",
@ -245,7 +249,7 @@ void TextWindow::ScreenOpacity(int link, uint32_t v) {
SS.TW.ShowEditControl(11, ssprintf("%.2f", g->color.alphaF())); SS.TW.ShowEditControl(11, ssprintf("%.2f", g->color.alphaF()));
SS.TW.edit.meaning = Edit::GROUP_OPACITY; SS.TW.edit.meaning = Edit::GROUP_OPACITY;
SS.TW.edit.group.v = g->h.v; SS.TW.edit.group = g->h;
} }
void TextWindow::ScreenChangeExprA(int link, uint32_t v) { void TextWindow::ScreenChangeExprA(int link, uint32_t v) {
Group *g = SK.GetGroup(SS.TW.shown.group); Group *g = SK.GetGroup(SS.TW.shown.group);
@ -271,7 +275,7 @@ void TextWindow::ScreenDeleteGroup(int link, uint32_t v) {
SS.UndoRemember(); SS.UndoRemember();
hGroup hg = SS.TW.shown.group; hGroup hg = SS.TW.shown.group;
if(hg.v == SS.GW.activeGroup.v) { if(hg == SS.GW.activeGroup) {
SS.GW.activeGroup = SK.GetGroup(SS.GW.activeGroup)->PreviousGroup()->h; SS.GW.activeGroup = SK.GetGroup(SS.GW.activeGroup)->PreviousGroup()->h;
} }
@ -291,7 +295,7 @@ void TextWindow::ShowGroupInfo() {
Group *g = SK.GetGroup(shown.group); Group *g = SK.GetGroup(shown.group);
const char *s = "???"; const char *s = "???";
if(shown.group.v == Group::HGROUP_REFERENCES.v) { if(shown.group == Group::HGROUP_REFERENCES) {
Printf(true, "%FtGROUP %E%s", g->DescriptionString().c_str()); Printf(true, "%FtGROUP %E%s", g->DescriptionString().c_str());
goto list_items; goto list_items;
} else { } else {
@ -304,14 +308,18 @@ void TextWindow::ShowGroupInfo() {
if(g->type == Group::Type::LATHE) { if(g->type == Group::Type::LATHE) {
Printf(true, " %Ftlathe plane sketch"); Printf(true, " %Ftlathe plane sketch");
} else if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::ROTATE || } else if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::ROTATE ||
g->type == Group::Type::TRANSLATE) g->type == Group::Type::TRANSLATE || g->type == Group::Type::REVOLVE ||
{ g->type == Group::Type::HELIX) {
if(g->type == Group::Type::EXTRUDE) { if(g->type == Group::Type::EXTRUDE) {
s = "extrude plane sketch"; s = "extrude plane sketch";
} else if(g->type == Group::Type::TRANSLATE) { } else if(g->type == Group::Type::TRANSLATE) {
s = "translate original sketch"; s = "translate original sketch";
} else if(g->type == Group::Type::HELIX) {
s = "create helical extrusion";
} else if(g->type == Group::Type::ROTATE) { } else if(g->type == Group::Type::ROTATE) {
s = "rotate original sketch"; s = "rotate original sketch";
} else if(g->type == Group::Type::REVOLVE) {
s = "revolve original sketch";
} }
Printf(true, " %Ft%s%E", s); Printf(true, " %Ft%s%E", s);
@ -362,10 +370,9 @@ void TextWindow::ShowGroupInfo() {
} }
Printf(false, ""); Printf(false, "");
if(g->type == Group::Type::EXTRUDE || if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
g->type == Group::Type::LATHE || g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED ||
g->type == Group::Type::LINKED) g->type == Group::Type::HELIX) {
{
bool un = (g->meshCombine == Group::CombineAs::UNION); bool un = (g->meshCombine == Group::CombineAs::UNION);
bool diff = (g->meshCombine == Group::CombineAs::DIFFERENCE); bool diff = (g->meshCombine == Group::CombineAs::DIFFERENCE);
bool asy = (g->meshCombine == Group::CombineAs::ASSEMBLE); bool asy = (g->meshCombine == Group::CombineAs::ASSEMBLE);
@ -384,9 +391,8 @@ void TextWindow::ShowGroupInfo() {
Group::CombineAs::ASSEMBLE, Group::CombineAs::ASSEMBLE,
(asy ? RADIO_TRUE : RADIO_FALSE)); (asy ? RADIO_TRUE : RADIO_FALSE));
if(g->type == Group::Type::EXTRUDE || if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
g->type == Group::Type::LATHE) g->type == Group::Type::REVOLVE || g->type == Group::Type::HELIX) {
{
Printf(false, Printf(false,
"%Bd %Ftcolor %E%Bz %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E", "%Bd %Ftcolor %E%Bz %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E",
&g->color, &g->color,
@ -397,9 +403,9 @@ void TextWindow::ShowGroupInfo() {
&TextWindow::ScreenOpacity); &TextWindow::ScreenOpacity);
} }
if(g->type == Group::Type::EXTRUDE || if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
g->type == Group::Type::LATHE || g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED ||
g->type == Group::Type::LINKED) { g->type == Group::Type::HELIX) {
Printf(false, " %Fd%f%LP%s suppress this group's solid model", Printf(false, " %Fd%f%LP%s suppress this group's solid model",
&TextWindow::ScreenChangeGroupOption, &TextWindow::ScreenChangeGroupOption,
g->suppress ? CHECK_TRUE : CHECK_FALSE); g->suppress ? CHECK_TRUE : CHECK_FALSE);
@ -443,16 +449,17 @@ list_items:
Printf(false, ""); Printf(false, "");
Printf(false, "%Ft requests in group"); Printf(false, "%Ft requests in group");
int i, a = 0; int a = 0;
for(i = 0; i < SK.request.n; i++) { for(auto &r : SK.request) {
Request *r = &(SK.request.elem[i]);
if(r->group.v == shown.group.v) { if(r.group == shown.group) {
std::string s = r->DescriptionString(); std::string s = r.DescriptionString();
Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", Printf(false, "%Bp %Fl%Ll%D%f%h%s%E",
(a & 1) ? 'd' : 'a', (a & 1) ? 'd' : 'a',
r->h.v, (&TextWindow::ScreenSelectRequest), r.h.v,
&(TextWindow::ScreenHoverRequest), s.c_str()); (&TextWindow::ScreenSelectRequest),
&(TextWindow::ScreenHoverRequest),
s.c_str());
a++; a++;
} }
} }
@ -461,16 +468,17 @@ list_items:
a = 0; a = 0;
Printf(false, ""); Printf(false, "");
Printf(false, "%Ft constraints in group (%d DOF)", g->solved.dof); Printf(false, "%Ft constraints in group (%d DOF)", g->solved.dof);
for(i = 0; i < SK.constraint.n; i++) { for(auto &c : SK.constraint) {
Constraint *c = &(SK.constraint.elem[i]);
if(c->group.v == shown.group.v) { if(c.group == shown.group) {
std::string s = c->DescriptionString(); std::string s = c.DescriptionString();
Printf(false, "%Bp %Fl%Ll%D%f%h%s%E %s", Printf(false, "%Bp %Fl%Ll%D%f%h%s%E %s",
(a & 1) ? 'd' : 'a', (a & 1) ? 'd' : 'a',
c->h.v, (&TextWindow::ScreenSelectConstraint), c.h.v,
(&TextWindow::ScreenHoverConstraint), s.c_str(), (&TextWindow::ScreenSelectConstraint),
c->reference ? "(ref)" : ""); (&TextWindow::ScreenHoverConstraint),
s.c_str(),
c.reference ? "(ref)" : "");
a++; a++;
} }
} }
@ -526,7 +534,7 @@ void TextWindow::ShowGroupSolveInfo() {
} }
for(int i = 0; i < g->solved.remove.n; i++) { for(int i = 0; i < g->solved.remove.n; i++) {
hConstraint hc = g->solved.remove.elem[i]; hConstraint hc = g->solved.remove[i];
Constraint *c = SK.constraint.FindByIdNoOops(hc); Constraint *c = SK.constraint.FindByIdNoOops(hc);
if(!c) continue; if(!c) continue;
@ -750,7 +758,7 @@ void TextWindow::EditControlDone(std::string s) {
Group *g = SK.group.FindByIdNoOops(SS.TW.shown.group); Group *g = SK.group.FindByIdNoOops(SS.TW.shown.group);
if(!g) break; if(!g) break;
g->color = RGBf(rgb.x, rgb.y, rgb.z); g->color = RgbaColor::FromFloat(rgb.x, rgb.y, rgb.z, g->color.alphaF());
SS.MarkGroupDirty(g->h); SS.MarkGroupDirty(g->h);
SS.GW.ClearSuper(); SS.GW.ClearSuper();

View File

@ -195,8 +195,8 @@ static Button *buttons[] = {
/** Foreground color codes. */ /** Foreground color codes. */
const TextWindow::Color TextWindow::fgColors[] = { const TextWindow::Color TextWindow::fgColors[] = {
{ 'd', RGBi(255, 255, 255) }, // Default : white { 'd', RGBi(255, 255, 255) }, // Default : white
{ 'l', RGBi(100, 100, 255) }, { 'l', RGBi(100, 200, 255) }, // links : blue
{ 't', RGBi(255, 200, 0) }, { 't', RGBi(255, 200, 100) }, // tree/text : yellow
{ 'h', RGBi( 90, 90, 90) }, { 'h', RGBi( 90, 90, 90) },
{ 's', RGBi( 40, 255, 40) }, // Ok : green { 's', RGBi( 40, 255, 40) }, // Ok : green
{ 'm', RGBi(200, 200, 0) }, { 'm', RGBi(200, 200, 0) },

View File

@ -151,6 +151,7 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my, UiCanvas *canvas,
int x = 17, y = (int)(height - 52); int x = 17, y = (int)(height - 52);
// When changing these values, also change the asReference drawing code in drawentity.cpp.
int fudge = 8; int fudge = 8;
int h = 34*16 + 3*16 + fudge; int h = 34*16 + 3*16 + fudge;
int aleft = 0, aright = 66, atop = y+16+fudge/2, abot = y+16-h; int aleft = 0, aright = 66, atop = y+16+fudge/2, abot = y+16-h;
@ -174,7 +175,7 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my, UiCanvas *canvas,
bool leftpos = true; bool leftpos = true;
for(ToolIcon &icon : Toolbar) { for(ToolIcon &icon : Toolbar) {
if(icon.name == "") { // spacer if(icon.name.empty()) { // spacer
if(!leftpos) { if(!leftpos) {
leftpos = true; leftpos = true;
y -= 32; y -= 32;

View File

@ -72,17 +72,17 @@ void TtfFontList::LoadAll() {
} }
// Sort fonts according to their actual name, not filename. // Sort fonts according to their actual name, not filename.
std::sort(&l.elem[0], &l.elem[l.n], std::sort(l.begin(), l.end(),
[](const TtfFont &a, const TtfFont &b) { return a.name < b.name; }); [](const TtfFont &a, const TtfFont &b) { return a.name < b.name; });
// Filter out fonts with the same family and style name. This is not // Filter out fonts with the same family and style name. This is not
// strictly necessarily the exact same font, but it will almost always be. // strictly necessarily the exact same font, but it will almost always be.
TtfFont *it = std::unique(&l.elem[0], &l.elem[l.n], TtfFont *it = std::unique(l.begin(), l.end(),
[](const TtfFont &a, const TtfFont &b) { return a.name == b.name; }); [](const TtfFont &a, const TtfFont &b) { return a.name == b.name; });
l.RemoveLast(&l.elem[l.n] - it); l.RemoveLast(&l[l.n] - it);
// TODO: identify fonts by their name and not filename, which may change //! @todo identify fonts by their name and not filename, which may change
// between OSes. //! between OSes.
loaded = true; loaded = true;
} }

View File

@ -125,7 +125,9 @@ enum class Command : uint32_t {
GROUP_3D, GROUP_3D,
GROUP_WRKPL, GROUP_WRKPL,
GROUP_EXTRUDE, GROUP_EXTRUDE,
GROUP_HELIX,
GROUP_LATHE, GROUP_LATHE,
GROUP_REVOLVE,
GROUP_ROT, GROUP_ROT,
GROUP_TRANS, GROUP_TRANS,
GROUP_LINK, GROUP_LINK,

View File

@ -36,8 +36,6 @@ void SolveSpaceUI::UndoEnableMenus() {
} }
void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) { void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
int i;
if(uk->cnt == MAX_UNDO) { if(uk->cnt == MAX_UNDO) {
UndoClearState(&(uk->d[uk->write])); UndoClearState(&(uk->d[uk->write]));
// And then write in to this one again // And then write in to this one again
@ -48,9 +46,9 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
UndoState *ut = &(uk->d[uk->write]); UndoState *ut = &(uk->d[uk->write]);
*ut = {}; *ut = {};
ut->group.ReserveMore(SK.group.n); ut->group.ReserveMore(SK.group.n);
for(i = 0; i < SK.group.n; i++) { for(Group &src : SK.group) {
Group *src = &(SK.group.elem[i]); // Shallow copy
Group dest = *src; Group dest(src);
// And then clean up all the stuff that needs to be a deep copy, // And then clean up all the stuff that needs to be a deep copy,
// and zero out all the dynamic stuff that will get regenerated. // and zero out all the dynamic stuff that will get regenerated.
dest.clean = false; dest.clean = false;
@ -66,42 +64,32 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
dest.displayMesh = {}; dest.displayMesh = {};
dest.displayOutlines = {}; dest.displayOutlines = {};
dest.remap = src->remap; dest.remap = src.remap;
dest.impMesh = {}; dest.impMesh = {};
dest.impShell = {}; dest.impShell = {};
dest.impEntity = {}; dest.impEntity = {};
ut->group.Add(&dest); ut->group.Add(&dest);
} }
for(i = 0; i < SK.groupOrder.n; i++) { for(auto &src : SK.groupOrder) { ut->groupOrder.Add(&src); }
ut->groupOrder.Add(&(SK.groupOrder.elem[i]));
}
ut->request.ReserveMore(SK.request.n); ut->request.ReserveMore(SK.request.n);
for(i = 0; i < SK.request.n; i++) { for(auto &src : SK.request) { ut->request.Add(&src); }
ut->request.Add(&(SK.request.elem[i]));
}
ut->constraint.ReserveMore(SK.constraint.n); ut->constraint.ReserveMore(SK.constraint.n);
for(i = 0; i < SK.constraint.n; i++) { for(auto &src : SK.constraint) {
Constraint *src = &(SK.constraint.elem[i]); // Shallow copy
Constraint dest = *src; Constraint dest(src);
ut->constraint.Add(&dest); ut->constraint.Add(&dest);
} }
ut->param.ReserveMore(SK.param.n); ut->param.ReserveMore(SK.param.n);
for(i = 0; i < SK.param.n; i++) { for(auto &src : SK.param) { ut->param.Add(&src); }
ut->param.Add(&(SK.param.elem[i]));
}
ut->style.ReserveMore(SK.style.n); ut->style.ReserveMore(SK.style.n);
for(i = 0; i < SK.style.n; i++) { for(auto &src : SK.style) { ut->style.Add(&src); }
ut->style.Add(&(SK.style.elem[i]));
}
ut->activeGroup = SS.GW.activeGroup; ut->activeGroup = SS.GW.activeGroup;
uk->write = WRAP(uk->write + 1, MAX_UNDO); uk->write = WRAP(uk->write + 1, MAX_UNDO);
} }
void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) { void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
int i;
ssassert(uk->cnt > 0, "Cannot pop from empty undo stack"); ssassert(uk->cnt > 0, "Cannot pop from empty undo stack");
(uk->cnt)--; (uk->cnt)--;
uk->write = WRAP(uk->write - 1, MAX_UNDO); uk->write = WRAP(uk->write - 1, MAX_UNDO);
@ -109,8 +97,8 @@ void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
UndoState *ut = &(uk->d[uk->write]); UndoState *ut = &(uk->d[uk->write]);
// Free everything in the main copy of the program before replacing it // Free everything in the main copy of the program before replacing it
for(i = 0; i < SK.groupOrder.n; i++) { for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]); Group *g = SK.GetGroup(hg);
g->Clear(); g->Clear();
} }
SK.group.Clear(); SK.group.Clear();
@ -122,8 +110,7 @@ void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
// And then do a shallow copy of the state from the undo list // And then do a shallow copy of the state from the undo list
ut->group.MoveSelfInto(&(SK.group)); ut->group.MoveSelfInto(&(SK.group));
for(i = 0; i < ut->groupOrder.n; i++) for(auto &gh : ut->groupOrder) { SK.groupOrder.Add(&gh); }
SK.groupOrder.Add(&ut->groupOrder.elem[i]);
ut->request.MoveSelfInto(&(SK.request)); ut->request.MoveSelfInto(&(SK.request));
ut->constraint.MoveSelfInto(&(SK.constraint)); ut->constraint.MoveSelfInto(&(SK.constraint));
ut->param.MoveSelfInto(&(SK.param)); ut->param.MoveSelfInto(&(SK.param));
@ -156,12 +143,7 @@ void SolveSpaceUI::UndoClearStack(UndoStack *uk) {
} }
void SolveSpaceUI::UndoClearState(UndoState *ut) { void SolveSpaceUI::UndoClearState(UndoState *ut) {
int i; for(auto &g : ut->group) { g.remap.clear(); }
for(i = 0; i < ut->group.n; i++) {
Group *g = &(ut->group.elem[i]);
g->remap.clear();
}
ut->group.Clear(); ut->group.Clear();
ut->request.Clear(); ut->request.Clear();
ut->constraint.Clear(); ut->constraint.Clear();

View File

@ -143,7 +143,17 @@ static void MessageBox(const char *fmt, va_list va, bool error,
description = description.substr(it - description.begin()); description = description.substr(it - description.begin());
Platform::MessageDialogRef dialog = CreateMessageDialog(SS.GW.window); Platform::MessageDialogRef dialog = CreateMessageDialog(SS.GW.window);
if (!dialog) {
if (error) {
fprintf(stderr, "Error: %s\n", message.c_str());
} else {
fprintf(stderr, "Message: %s\n", message.c_str());
}
if(onDismiss) {
onDismiss();
}
return;
}
using Platform::MessageDialog; using Platform::MessageDialog;
if(error) { if(error) {
dialog->SetType(MessageDialog::Type::ERROR); dialog->SetType(MessageDialog::Type::ERROR);
@ -597,7 +607,7 @@ bool Vector::OnLineSegment(Vector a, Vector b, double tol) const {
if(distsq >= tol*tol) return false; if(distsq >= tol*tol) return false;
double t = (this->Minus(a)).DivPivoting(d); double t = (this->Minus(a)).DivProjected(d);
// On-endpoint already tested // On-endpoint already tested
if(t < 0 || t > 1) return false; if(t < 0 || t > 1) return false;
return true; return true;
@ -686,16 +696,9 @@ Vector4 Vector::Project4d() const {
return Vector4::From(1, x, y, z); return Vector4::From(1, x, y, z);
} }
double Vector::DivPivoting(Vector delta) const { double Vector::DivProjected(Vector delta) const {
double mx = fabs(delta.x), my = fabs(delta.y), mz = fabs(delta.z); return (x*delta.x + y*delta.y + z*delta.z)
/ (delta.x*delta.x + delta.y*delta.y + delta.z*delta.z);
if(mx > my && mx > mz) {
return x/delta.x;
} else if(my > mz) {
return y/delta.y;
} else {
return z/delta.z;
}
} }
Vector Vector::ClosestOrtho() const { Vector Vector::ClosestOrtho() const {
@ -985,12 +988,8 @@ Point2d Point2d::ScaledBy(double s) const {
return { x * s, y * s }; return { x * s, y * s };
} }
double Point2d::DivPivoting(Point2d delta) const { double Point2d::DivProjected(Point2d delta) const {
if(fabs(delta.x) > fabs(delta.y)) { return (x*delta.x + y*delta.y) / (delta.x*delta.x + delta.y*delta.y);
return x/delta.x;
} else {
return y/delta.y;
}
} }
double Point2d::MagSquared() const { double Point2d::MagSquared() const {

View File

@ -363,6 +363,7 @@ int main(int argc, char **argv) {
} }
SS.Init(); SS.Init();
SS.showToolbar = false;
SS.checkClosedContour = false; SS.checkClosedContour = false;
Test::Helper helper = {}; Test::Helper helper = {};